lkml.org 
[lkml]   [2020]   [Mar]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 21/24] firmware: xilinx: Add sysfs interface
    Date
    From: Rajan Vaja <rajan.vaja@xilinx.com>

    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: Jolly Shah <jollys@xilinx.com>
    Signed-off-by: Tejas Patel <tejas.patel@xilinx.com>
    Signed-off-by: Jolly Shah <jolly.shah@xilinx.com>
    ---
    .../ABI/stable/sysfs-driver-firmware-zynqmp | 50 ++++
    drivers/firmware/xilinx/zynqmp.c | 319 ++++++++++++++++++++-
    include/linux/firmware/xlnx-zynqmp.h | 4 +
    3 files changed, 372 insertions(+), 1 deletion(-)
    create mode 100644 Documentation/ABI/stable/sysfs-driver-firmware-zynqmp

    diff --git a/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp b/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
    new file mode 100644
    index 0000000..7fd6e70
    --- /dev/null
    +++ b/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
    @@ -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 <mask> <value> > /sys/devices/platform/firmware\:zynqmp-firmware/ggs0
    +
    + Example:
    + # cat /sys/devices/platform/firmware\:zynqmp-firmware/ggs0
    + # echo 0xFFFFFFFF 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 <mask> <value> > /sys/devices/platform/firmware\:zynqmp-firmware/pggs0
    +
    + Example:
    + # cat /sys/devices/platform/firmware\:zynqmp-firmware/pggs0
    + # echo 0xFFFFFFFF 0x1234ABCD > /sys/devices/platform/firmware\:zynqmp-firmware/pggs0
    +
    +Users: Xilinx
    diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
    index de99613..f671b6b 100644
    --- a/drivers/firmware/xilinx/zynqmp.c
    +++ b/drivers/firmware/xilinx/zynqmp.c
    @@ -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>
    @@ -833,6 +833,322 @@ int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities,
    }
    EXPORT_SYMBOL_GPL(zynqmp_pm_set_requirement);

    +/**
    + * ggs_show - Show global general storage (ggs) sysfs attribute
    + * @device: Device structure
    + * @attr: Device attribute structure
    + * @buf: Requested available shutdown_scope attributes string
    + * @reg: Register number
    + *
    + * Return:Number of bytes printed into the buffer.
    + *
    + * Helper function for viewing a ggs register value.
    + *
    + * User-space interface for viewing the content of the ggs0 register.
    + * cat /sys/devices/platform/firmware\:zynqmp-firmware/ggs0
    + */
    +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]);
    +}
    +
    +/**
    + * ggs_store - Store global general storage (ggs) sysfs attribute
    + * @device: Device structure
    + * @attr: Device attribute structure
    + * @buf: User entered shutdown_scope attribute string
    + * @count: Size of buf
    + * @reg: Register number
    + *
    + * Return: count argument if request succeeds, the corresponding
    + * error code otherwise
    + *
    + * Helper function for storing a ggs register value.
    + *
    + * For example, the user-space interface for storing a value to the
    + * ggs0 register:
    + * echo 0xFFFFFFFF 0x1234ABCD > /sys/devices/platform/firmware\:zynqmp-firmware/ggs0
    + */
    +static ssize_t ggs_store(struct device *device,
    + struct device_attribute *attr,
    + const char *buf, size_t count,
    + u32 reg)
    +{
    + char *kern_buff, *inbuf, *tok;
    + long mask, value;
    + int ret;
    + u32 ret_payload[PAYLOAD_ARG_CNT];
    +
    + if (!device || !attr || !buf || !count || reg >= GSS_NUM_REGS)
    + return -EINVAL;
    +
    + kern_buff = kzalloc(count, GFP_KERNEL);
    + if (!kern_buff)
    + return -ENOMEM;
    +
    + ret = strlcpy(kern_buff, buf, count);
    + if (ret < 0) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + inbuf = kern_buff;
    +
    + /* Read the write mask */
    + tok = strsep(&inbuf, " ");
    + if (!tok) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + ret = kstrtol(tok, 16, &mask);
    + if (ret) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + /* Read the write value */
    + tok = strsep(&inbuf, " ");
    + if (!tok) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + ret = kstrtol(tok, 16, &value);
    + if (ret) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + ret = zynqmp_pm_read_ggs(reg, ret_payload);
    + if (ret) {
    + count = -EFAULT;
    + goto err;
    + }
    + ret_payload[1] &= ~mask;
    + value &= mask;
    + value |= ret_payload[1];
    +
    + ret = zynqmp_pm_write_ggs(reg, value);
    + if (ret)
    + count = -EFAULT;
    +
    +err:
    + kfree(kern_buff);
    +
    + 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);
    +
    +/**
    + * pggs_show - Show persistent global general storage (pggs) sysfs attribute
    + * @device: Device structure
    + * @attr: Device attribute structure
    + * @buf: Requested available shutdown_scope attributes string
    + * @reg: Register number
    + *
    + * Return:Number of bytes printed into the buffer.
    + *
    + * Helper function for viewing a pggs register value.
    + */
    +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]);
    +}
    +
    +/**
    + * pggs_store - Store persistent global general storage (pggs) sysfs attribute
    + * @device: Device structure
    + * @attr: Device attribute structure
    + * @buf: User entered shutdown_scope attribute string
    + * @count: Size of buf
    + * @reg: Register number
    + *
    + * Return: count argument if request succeeds, the corresponding
    + * error code otherwise
    + *
    + * Helper function for storing a pggs register value.
    + */
    +static ssize_t pggs_store(struct device *device,
    + struct device_attribute *attr,
    + const char *buf, size_t count,
    + u32 reg)
    +{
    + char *kern_buff, *inbuf, *tok;
    + long mask, value;
    + int ret;
    + u32 ret_payload[PAYLOAD_ARG_CNT];
    +
    + if (!device || !attr || !buf || !count || reg >= GSS_NUM_REGS)
    + return -EINVAL;
    +
    + kern_buff = kzalloc(count, GFP_KERNEL);
    + if (!kern_buff)
    + return -ENOMEM;
    +
    + ret = strlcpy(kern_buff, buf, count);
    + if (ret < 0) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + inbuf = kern_buff;
    +
    + /* Read the write mask */
    + tok = strsep(&inbuf, " ");
    + if (!tok) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + ret = kstrtol(tok, 16, &mask);
    + if (ret) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + /* Read the write value */
    + tok = strsep(&inbuf, " ");
    + if (!tok) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + ret = kstrtol(tok, 16, &value);
    + if (ret) {
    + count = -EFAULT;
    + goto err;
    + }
    +
    + ret = zynqmp_pm_read_pggs(reg, ret_payload);
    + if (ret) {
    + count = -EFAULT;
    + goto err;
    + }
    + ret_payload[1] &= ~mask;
    + value &= mask;
    + value |= ret_payload[1];
    +
    + ret = zynqmp_pm_write_pggs(reg, value);
    + if (ret)
    + count = -EFAULT;
    +
    +err:
    + kfree(kern_buff);
    +
    + 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_ggs_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,
    +};
    +
    +static const struct attribute_group ggs_attribute_group = {
    + .attrs = zynqmp_ggs_attrs,
    +};
    +
    +const struct attribute_group *firmware_attribute_groups[] = {
    + &ggs_attribute_group,
    + NULL,
    +};
    +
    static int zynqmp_firmware_probe(struct platform_device *pdev)
    {
    struct device *dev = &pdev->dev;
    @@ -910,6 +1226,7 @@ static struct platform_driver zynqmp_firmware_driver = {
    .driver = {
    .name = "zynqmp_firmware",
    .of_match_table = zynqmp_firmware_of_match,
    + .dev_groups = firmware_attribute_groups,
    },
    .probe = zynqmp_firmware_probe,
    .remove = zynqmp_firmware_remove,
    diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
    index 725dccf..8ccaa39 100644
    --- a/include/linux/firmware/xlnx-zynqmp.h
    +++ b/include/linux/firmware/xlnx-zynqmp.h
    @@ -13,6 +13,8 @@
    #ifndef __FIRMWARE_ZYNQMP_H__
    #define __FIRMWARE_ZYNQMP_H__

    +#include <linux/device.h>
    +
    #define ZYNQMP_PM_VERSION_MAJOR 1
    #define ZYNQMP_PM_VERSION_MINOR 0

    @@ -42,6 +44,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
    --
    2.7.4
    \
     
     \ /
      Last update: 2020-03-07 00:49    [W:4.709 / U:0.364 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site