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

firmware: xilinx: Add sysfs interface



Add firmware-ggs sysfs interface which provides read/write
interface to global storage registers.

Signed-off-by: default avatarRajan Vaja <rajan.vaja@xilinx.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-23-git-send-email-jolly.shah@xilinx.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4f680b72
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
What:		/sys/devices/platform/firmware\:zynqmp-firmware/ggs*
Date:		March 2020
KernelVersion:	5.6
Contact:	"Jolly Shah" <jollys@xilinx.com>
Description:
		Read/Write PMU global general storage register value,
		GLOBAL_GEN_STORAGE{0:3}.
		Global general storage register that can be used
		by system to pass information between masters.

		The register is reset during system or power-on
		resets. Three registers are used by the FSBL and
		other Xilinx software products: GLOBAL_GEN_STORAGE{4:6}.

		Usage:
		# cat /sys/devices/platform/firmware\:zynqmp-firmware/ggs0
		# echo <value> > /sys/devices/platform/firmware\:zynqmp-firmware/ggs0

		Example:
		# cat /sys/devices/platform/firmware\:zynqmp-firmware/ggs0
		# echo 0x1234ABCD > /sys/devices/platform/firmware\:zynqmp-firmware/ggs0

Users:		Xilinx

What:		/sys/devices/platform/firmware\:zynqmp-firmware/pggs*
Date:		March 2020
KernelVersion:	5.6
Contact:	"Jolly Shah" <jollys@xilinx.com>
Description:
		Read/Write PMU persistent global general storage register
		value, PERS_GLOB_GEN_STORAGE{0:3}.
		Persistent global general storage register that
		can be used by system to pass information between
		masters.

		This register is only reset by the power-on reset
		and maintains its value through a system reset.
		Four registers are used by the FSBL and other Xilinx
		software products: PERS_GLOB_GEN_STORAGE{4:7}.
		Register is reset only by a POR reset.

		Usage:
		# cat /sys/devices/platform/firmware\:zynqmp-firmware/pggs0
		# echo <value> > /sys/devices/platform/firmware\:zynqmp-firmware/pggs0

		Example:
		# cat /sys/devices/platform/firmware\:zynqmp-firmware/pggs0
		# echo 0x1234ABCD > /sys/devices/platform/firmware\:zynqmp-firmware/pggs0

Users:		Xilinx
+166 −1
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/*
 * Xilinx Zynq MPSoC Firmware layer
 *
 *  Copyright (C) 2014-2018 Xilinx, Inc.
 *  Copyright (C) 2014-2020 Xilinx, Inc.
 *
 *  Michal Simek <michal.simek@xilinx.com>
 *  Davorin Mista <davorin.mista@aggios.com>
@@ -875,6 +875,170 @@ int zynqmp_pm_aes_engine(const u64 address, u32 *out)
}
EXPORT_SYMBOL_GPL(zynqmp_pm_aes_engine);

static ssize_t ggs_show(struct device *device,
			struct device_attribute *attr,
			char *buf,
			u32 reg)
{
	int ret;
	u32 ret_payload[PAYLOAD_ARG_CNT];

	ret = zynqmp_pm_read_ggs(reg, ret_payload);
	if (ret)
		return ret;

	return sprintf(buf, "0x%x\n", ret_payload[1]);
}

static ssize_t ggs_store(struct device *device,
			 struct device_attribute *attr,
			 const char *buf, size_t count,
			 u32 reg)
{
	long value;
	int ret;

	if (reg >= GSS_NUM_REGS)
		return -EINVAL;

	ret = kstrtol(buf, 16, &value);
	if (ret) {
		count = -EFAULT;
		goto err;
	}

	ret = zynqmp_pm_write_ggs(reg, value);
	if (ret)
		count = -EFAULT;

err:
	return count;
}

/* GGS register show functions */
#define GGS0_SHOW(N)						\
	ssize_t ggs##N##_show(struct device *device,		\
			      struct device_attribute *attr,	\
			      char *buf)			\
	{							\
		return ggs_show(device, attr, buf, N);		\
	}

static GGS0_SHOW(0);
static GGS0_SHOW(1);
static GGS0_SHOW(2);
static GGS0_SHOW(3);

/* GGS register store function */
#define GGS0_STORE(N)						\
	ssize_t ggs##N##_store(struct device *device,		\
			       struct device_attribute *attr,	\
			       const char *buf,			\
			       size_t count)			\
	{							\
		return ggs_store(device, attr, buf, count, N);	\
	}

static GGS0_STORE(0);
static GGS0_STORE(1);
static GGS0_STORE(2);
static GGS0_STORE(3);

static ssize_t pggs_show(struct device *device,
			 struct device_attribute *attr,
			 char *buf,
			 u32 reg)
{
	int ret;
	u32 ret_payload[PAYLOAD_ARG_CNT];

	ret = zynqmp_pm_read_pggs(reg, ret_payload);
	if (ret)
		return ret;

	return sprintf(buf, "0x%x\n", ret_payload[1]);
}

static ssize_t pggs_store(struct device *device,
			  struct device_attribute *attr,
			  const char *buf, size_t count,
			  u32 reg)
{
	long value;
	int ret;

	if (reg >= GSS_NUM_REGS)
		return -EINVAL;

	ret = kstrtol(buf, 16, &value);
	if (ret) {
		count = -EFAULT;
		goto err;
	}

	ret = zynqmp_pm_write_pggs(reg, value);
	if (ret)
		count = -EFAULT;

err:
	return count;
}

#define PGGS0_SHOW(N)						\
	ssize_t pggs##N##_show(struct device *device,		\
			       struct device_attribute *attr,	\
			       char *buf)			\
	{							\
		return pggs_show(device, attr, buf, N);		\
	}

#define PGGS0_STORE(N)						\
	ssize_t pggs##N##_store(struct device *device,		\
				struct device_attribute *attr,	\
				const char *buf,		\
				size_t count)			\
	{							\
		return pggs_store(device, attr, buf, count, N);	\
	}

/* PGGS register show functions */
static PGGS0_SHOW(0);
static PGGS0_SHOW(1);
static PGGS0_SHOW(2);
static PGGS0_SHOW(3);

/* PGGS register store functions */
static PGGS0_STORE(0);
static PGGS0_STORE(1);
static PGGS0_STORE(2);
static PGGS0_STORE(3);

/* GGS register attributes */
static DEVICE_ATTR_RW(ggs0);
static DEVICE_ATTR_RW(ggs1);
static DEVICE_ATTR_RW(ggs2);
static DEVICE_ATTR_RW(ggs3);

/* PGGS register attributes */
static DEVICE_ATTR_RW(pggs0);
static DEVICE_ATTR_RW(pggs1);
static DEVICE_ATTR_RW(pggs2);
static DEVICE_ATTR_RW(pggs3);

static struct attribute *zynqmp_firmware_attrs[] = {
	&dev_attr_ggs0.attr,
	&dev_attr_ggs1.attr,
	&dev_attr_ggs2.attr,
	&dev_attr_ggs3.attr,
	&dev_attr_pggs0.attr,
	&dev_attr_pggs1.attr,
	&dev_attr_pggs2.attr,
	&dev_attr_pggs3.attr,
	NULL,
};

ATTRIBUTE_GROUPS(zynqmp_firmware);

static int zynqmp_firmware_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
@@ -952,6 +1116,7 @@ static struct platform_driver zynqmp_firmware_driver = {
	.driver = {
		.name = "zynqmp_firmware",
		.of_match_table = zynqmp_firmware_of_match,
		.dev_groups = zynqmp_firmware_groups,
	},
	.probe = zynqmp_firmware_probe,
	.remove = zynqmp_firmware_remove,
+2 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@

#define ZYNQMP_PM_MAX_QOS		100U

#define GSS_NUM_REGS	(4)

/* Node capabilities */
#define	ZYNQMP_PM_CAPABILITY_ACCESS	0x1U
#define	ZYNQMP_PM_CAPABILITY_CONTEXT	0x2U