firmware: xilinx: Add sysfs interface
authorRajan Vaja <rajan.vaja@xilinx.com>
Fri, 24 Apr 2020 20:58:04 +0000 (13:58 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Apr 2020 13:46:54 +0000 (15:46 +0200)
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>
Documentation/ABI/stable/sysfs-driver-firmware-zynqmp [new file with mode: 0644]
drivers/firmware/xilinx/zynqmp.c
include/linux/firmware/xlnx-zynqmp.h

diff --git a/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp b/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
new file mode 100644 (file)
index 0000000..2e3aebd
--- /dev/null
@@ -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
index 3518456..2fe4f57 100644 (file)
@@ -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,
index e23251d..c1356e9 100644 (file)
@@ -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