Commit b3ae24c4 authored by Rajan Vaja's avatar Rajan Vaja Committed by Greg Kroah-Hartman
Browse files

firmware: xilinx: Add sysfs to set shutdown scope



The Linux shutdown functionality implemented via PSCI system_off does
not include an option to set a scope, i.e. which parts of the system to
shut down.

This patch creates sysfs that allows to set the shutdown scope for the
next shutdown request. When the next shutdown is performed, the platform
specific portion of PSCI-system_off can use the chosen shutdown scope.

Signed-off-by: default avatarRajan Vaja <rajan.vaja@xilinx.com>
Signed-off-by: default avatarStefan Krsmanovic <stefan.krsmanovic@aggios.com>
Signed-off-by: default avatarMichal Simek <michal.simek@xilinx.com>
Signed-off-by: default avatarTejas Patel <tejas.patel@xilinx.com>
Signed-off-by: default avatarJolly Shah <jolly.shah@xilinx.com>
Link: https://lore.kernel.org/r/1587761887-4279-25-git-send-email-jolly.shah@xilinx.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent fdd2ed88
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -48,3 +48,35 @@ Description:
		# echo 0x1234ABCD > /sys/devices/platform/firmware\:zynqmp-firmware/pggs0

Users:		Xilinx

What:		/sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
Date:		March 2020
KernelVersion:	5.6
Contact:	"Jolly Shah" <jollys@xilinx.com>
Description:
		This sysfs interface allows to set the shutdown scope for the
		next shutdown request. When the next shutdown is performed, the
		platform specific portion of PSCI-system_off can use the chosen
		shutdown scope.

		Following are available shutdown scopes(subtypes):

		subsystem:	Only the APU along with all of its peripherals
				not used by other processing units will be
				shut down. This may result in the FPD power
				domain being shut down provided that no other
				processing unit uses FPD peripherals or DRAM.
		ps_only:	The complete PS will be shut down, including the
				RPU, PMU, etc.  Only the PL domain (FPGA)
				remains untouched.
		system:		The complete system/device is shut down.

		Usage:
		# cat /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
		# echo <scope> > /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope

		Example:
		# cat /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
		# echo "subsystem" > /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope

Users:		Xilinx
+97 −1
Original line number Diff line number Diff line
@@ -888,6 +888,102 @@ int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype)
				   0, 0, NULL);
}

/**
 * struct zynqmp_pm_shutdown_scope - Struct for shutdown scope
 * @subtype:	Shutdown subtype
 * @name:	Matching string for scope argument
 *
 * This struct encapsulates mapping between shutdown scope ID and string.
 */
struct zynqmp_pm_shutdown_scope {
	const enum zynqmp_pm_shutdown_subtype subtype;
	const char *name;
};

static struct zynqmp_pm_shutdown_scope shutdown_scopes[] = {
	[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM] = {
		.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM,
		.name = "subsystem",
	},
	[ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY] = {
		.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY,
		.name = "ps_only",
	},
	[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM] = {
		.subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM,
		.name = "system",
	},
};

static struct zynqmp_pm_shutdown_scope *selected_scope =
		&shutdown_scopes[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM];

/**
 * zynqmp_pm_is_shutdown_scope_valid - Check if shutdown scope string is valid
 * @scope_string:	Shutdown scope string
 *
 * Return:		Return pointer to matching shutdown scope struct from
 *			array of available options in system if string is valid,
 *			otherwise returns NULL.
 */
static struct zynqmp_pm_shutdown_scope*
		zynqmp_pm_is_shutdown_scope_valid(const char *scope_string)
{
	int count;

	for (count = 0; count < ARRAY_SIZE(shutdown_scopes); count++)
		if (sysfs_streq(scope_string, shutdown_scopes[count].name))
			return &shutdown_scopes[count];

	return NULL;
}

static ssize_t shutdown_scope_show(struct device *device,
				   struct device_attribute *attr,
				   char *buf)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(shutdown_scopes); i++) {
		if (&shutdown_scopes[i] == selected_scope) {
			strcat(buf, "[");
			strcat(buf, shutdown_scopes[i].name);
			strcat(buf, "]");
		} else {
			strcat(buf, shutdown_scopes[i].name);
		}
		strcat(buf, " ");
	}
	strcat(buf, "\n");

	return strlen(buf);
}

static ssize_t shutdown_scope_store(struct device *device,
				    struct device_attribute *attr,
				    const char *buf, size_t count)
{
	int ret;
	struct zynqmp_pm_shutdown_scope *scope;

	scope = zynqmp_pm_is_shutdown_scope_valid(buf);
	if (!scope)
		return -EINVAL;

	ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY,
					scope->subtype);
	if (ret) {
		pr_err("unable to set shutdown scope %s\n", buf);
		return ret;
	}

	selected_scope = scope;

	return count;
}

static DEVICE_ATTR_RW(shutdown_scope);

static ssize_t ggs_show(struct device *device,
			struct device_attribute *attr,
			char *buf,
@@ -923,7 +1019,6 @@ static ssize_t ggs_store(struct device *device,
	ret = zynqmp_pm_write_ggs(reg, value);
	if (ret)
		count = -EFAULT;

err:
	return count;
}
@@ -1047,6 +1142,7 @@ static struct attribute *zynqmp_firmware_attrs[] = {
	&dev_attr_pggs1.attr,
	&dev_attr_pggs2.attr,
	&dev_attr_pggs3.attr,
	&dev_attr_shutdown_scope.attr,
	NULL,
};

+12 −0
Original line number Diff line number Diff line
@@ -286,6 +286,18 @@ enum dll_reset_type {
	PM_DLL_RESET_PULSE,
};

enum zynqmp_pm_shutdown_type {
	ZYNQMP_PM_SHUTDOWN_TYPE_SHUTDOWN,
	ZYNQMP_PM_SHUTDOWN_TYPE_RESET,
	ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY,
};

enum zynqmp_pm_shutdown_subtype {
	ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM,
	ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY,
	ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM,
};

/**
 * struct zynqmp_pm_query_data - PM query data
 * @qid:	query ID