firmware: xilinx: Add sysfs interface
Add firmware-ggs sysfs interface which provides read/write interface to global storage registers. Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com> Signed-off-by: Michal Simek <michal.simek@xilinx.com> Signed-off-by: Tejas Patel <tejas.patel@xilinx.com> Signed-off-by: Jolly Shah <jolly.shah@xilinx.com> Link: https://lore.kernel.org/r/1587761887-4279-23-git-send-email-jolly.shah@xilinx.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
4f680b72ea
commit
ae5c961da6
|
@ -0,0 +1,50 @@
|
|||
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
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue