Messages in this thread |  | | Subject | Re: [PATCH 2/5] firmware: xilinx: Add sysfs interface | From | Michal Simek <> | Date | Wed, 18 Dec 2019 15:21:21 +0100 |
| |
On 05. 12. 19 0:29, Jolly Shah wrote: > From: Rajan Vaja <rajan.vaja@xilinx.com> > > Add Firmware-ggs sysfs interface which provides read/write > interface to global storage registers. > > Signed-off-by: Michal Simek <michal.simek@xilinx.com> > Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com> > Signed-off-by: Jolly Shah <jolly.shah@xilinx.com> > --- > Documentation/ABI/stable/sysfs-firmware-zynqmp | 50 +++++ > drivers/firmware/xilinx/Makefile | 2 +- > drivers/firmware/xilinx/zynqmp-ggs.c | 289 +++++++++++++++++++++++++ > drivers/firmware/xilinx/zynqmp.c | 12 + > include/linux/firmware/xlnx-zynqmp.h | 10 + > 5 files changed, 362 insertions(+), 1 deletion(-) > create mode 100644 Documentation/ABI/stable/sysfs-firmware-zynqmp > create mode 100644 drivers/firmware/xilinx/zynqmp-ggs.c > > diff --git a/Documentation/ABI/stable/sysfs-firmware-zynqmp b/Documentation/ABI/stable/sysfs-firmware-zynqmp > new file mode 100644 > index 0000000..0a75812 > --- /dev/null > +++ b/Documentation/ABI/stable/sysfs-firmware-zynqmp > @@ -0,0 +1,50 @@ > +What: /sys/firmware/zynqmp/ggs* > +Date: January 2018 > +KernelVersion: 4.15.0
looks old.
> +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/firmware/zynqmp/ggs0 > + # echo <mask> <value> > /sys/firmware/zynqmp/ggs0 > + > + Example: > + # cat /sys/firmware/zynqmp/ggs0 > + # echo 0xFFFFFFFF 0x1234ABCD > /sys/firmware/zynqmp/ggs0 > + > +Users: Xilinx > + > +What: /sys/firmware/zynqmp/pggs* > +Date: January 2018 > +KernelVersion: 4.15.0
ditto.
> +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/firmware/zynqmp/pggs0 > + # echo <mask> <value> > /sys/firmware/zynqmp/pggs0 > + > + Example: > + # cat /sys/firmware/zynqmp/pggs0 > + # echo 0xFFFFFFFF 0x1234ABCD > /sys/firmware/zynqmp/pggs0 > + > +Users: Xilinx > diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile > index 875a537..1e8643c 100644 > --- a/drivers/firmware/xilinx/Makefile > +++ b/drivers/firmware/xilinx/Makefile > @@ -1,5 +1,5 @@ > # SPDX-License-Identifier: GPL-2.0 > # Makefile for Xilinx firmwares > > -obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o > +obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o zynqmp-ggs.o > obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o > diff --git a/drivers/firmware/xilinx/zynqmp-ggs.c b/drivers/firmware/xilinx/zynqmp-ggs.c > new file mode 100644 > index 0000000..42179ad > --- /dev/null > +++ b/drivers/firmware/xilinx/zynqmp-ggs.c > @@ -0,0 +1,289 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Xilinx Zynq MPSoC Firmware layer > + * > + * Copyright (C) 2014-2018 Xilinx, Inc. > + * > + * Jolly Shah <jollys@xilinx.com> > + * Rajan Vaja <rajanv@xilinx.com> > + */ > + > +#include <linux/compiler.h> > +#include <linux/of.h> > +#include <linux/init.h> > +#include <linux/module.h> > +#include <linux/uaccess.h> > +#include <linux/slab.h> > +#include <linux/firmware/xlnx-zynqmp.h> > + > +static ssize_t read_register(char *buf, u32 ioctl_id, u32 reg) > +{ > + int ret; > + u32 ret_payload[PAYLOAD_ARG_CNT]; > + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); > + > + if (!eemi_ops->ioctl) > + return -EFAULT; > + > + ret = eemi_ops->ioctl(0, ioctl_id, reg, 0, ret_payload); > + if (ret) > + return ret; > + > + return sprintf(buf, "0x%x\n", ret_payload[1]); > +} > + > +static ssize_t write_register(const char *buf, size_t count, u32 read_ioctl, > + u32 write_ioctl, u32 reg) > +{ > + char *kern_buff, *inbuf, *tok; > + long mask, value; > + int ret; > + u32 ret_payload[PAYLOAD_ARG_CNT]; > + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); > + > + if (!eemi_ops->ioctl) > + return -EFAULT; > + > + kern_buff = kzalloc(count, GFP_KERNEL); > + if (!kern_buff) > + return -ENOMEM; > + > + ret = strlcpy(kern_buff, buf, count); > + if (ret < 0) { > + ret = -EFAULT; > + goto err; > + } > + > + inbuf = kern_buff; > + > + /* Read the write mask */ > + tok = strsep(&inbuf, " "); > + if (!tok) { > + ret = -EFAULT; > + goto err; > + } > + > + ret = kstrtol(tok, 16, &mask); > + if (ret) { > + ret = -EFAULT; > + goto err; > + } > + > + /* Read the write value */ > + tok = strsep(&inbuf, " "); > + if (!tok) { > + ret = -EFAULT; > + goto err; > + } > + > + ret = kstrtol(tok, 16, &value); > + if (ret) { > + ret = -EFAULT; > + goto err; > + } > + > + ret = eemi_ops->ioctl(0, read_ioctl, reg, 0, ret_payload); > + if (ret) { > + ret = -EFAULT; > + goto err; > + } > + ret_payload[1] &= ~mask; > + value &= mask; > + value |= ret_payload[1]; > + > + ret = eemi_ops->ioctl(0, write_ioctl, reg, value, NULL); > + if (ret) > + ret = -EFAULT; > + > +err: > + kfree(kern_buff); > + if (ret) > + return ret; > + > + return count; > +} > + > +/** > + * ggs_show - Show global general storage (ggs) sysfs attribute > + * @kobj: Kobject structure > + * @attr: Kobject 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/firmware/zynqmp/ggs0 > + */ > +static ssize_t ggs_show(struct kobject *kobj, > + struct kobj_attribute *attr, > + char *buf, > + u32 reg) > +{ > + return read_register(buf, IOCTL_READ_GGS, reg); > +} > + > +/** > + * ggs_store - Store global general storage (ggs) sysfs attribute > + * @kobj: Kobject structure > + * @attr: Kobject 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/firmware/zynqmp/ggs0 > + */ > +static ssize_t ggs_store(struct kobject *kobj, > + struct kobj_attribute *attr, > + const char *buf, > + size_t count, > + u32 reg) > +{ > + if (!kobj || !attr || !buf || !count || reg >= GSS_NUM_REGS) > + return -EINVAL; > + > + return write_register(buf, count, IOCTL_READ_GGS, IOCTL_WRITE_GGS, reg); > +} > + > +/* GGS register show functions */ > +#define GGS0_SHOW(N) \ > + ssize_t ggs##N##_show(struct kobject *kobj, \ > + struct kobj_attribute *attr, \ > + char *buf) \ > + { \ > + return ggs_show(kobj, 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 kobject *kobj, \ > + struct kobj_attribute *attr, \ > + const char *buf, \ > + size_t count) \ > + { \ > + return ggs_store(kobj, 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 > + * @kobj: Kobject structure > + * @attr: Kobject 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 kobject *kobj, > + struct kobj_attribute *attr, > + char *buf, > + u32 reg) > +{ > + return read_register(buf, IOCTL_READ_PGGS, reg); > +} > + > +/** > + * pggs_store - Store persistent global general storage (pggs) sysfs attribute > + * @kobj: Kobject structure > + * @attr: Kobject 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 kobject *kobj, > + struct kobj_attribute *attr, > + const char *buf, > + size_t count, > + u32 reg) > +{ > + return write_register(buf, count, IOCTL_READ_PGGS, > + IOCTL_WRITE_PGGS, reg); > +} > + > +#define PGGS0_SHOW(N) \ > + ssize_t pggs##N##_show(struct kobject *kobj, \ > + struct kobj_attribute *attr, \ > + char *buf) \ > + { \ > + return pggs_show(kobj, attr, buf, N); \ > + } > + > +#define PGGS0_STORE(N) \ > + ssize_t pggs##N##_store(struct kobject *kobj, \ > + struct kobj_attribute *attr, \ > + const char *buf, \ > + size_t count) \ > + { \ > + return pggs_store(kobj, 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 struct kobj_attribute zynqmp_attr_ggs0 = __ATTR_RW(ggs0); > +static struct kobj_attribute zynqmp_attr_ggs1 = __ATTR_RW(ggs1); > +static struct kobj_attribute zynqmp_attr_ggs2 = __ATTR_RW(ggs2); > +static struct kobj_attribute zynqmp_attr_ggs3 = __ATTR_RW(ggs3); > + > +/* PGGS register attributes */ > +static struct kobj_attribute zynqmp_attr_pggs0 = __ATTR_RW(pggs0); > +static struct kobj_attribute zynqmp_attr_pggs1 = __ATTR_RW(pggs1); > +static struct kobj_attribute zynqmp_attr_pggs2 = __ATTR_RW(pggs2); > +static struct kobj_attribute zynqmp_attr_pggs3 = __ATTR_RW(pggs3); > + > +static struct attribute *attrs[] = { > + &zynqmp_attr_ggs0.attr, > + &zynqmp_attr_ggs1.attr, > + &zynqmp_attr_ggs2.attr, > + &zynqmp_attr_ggs3.attr, > + &zynqmp_attr_pggs0.attr, > + &zynqmp_attr_pggs1.attr, > + &zynqmp_attr_pggs2.attr, > + &zynqmp_attr_pggs3.attr, > + NULL, > +}; > + > +static const struct attribute_group attr_group = { > + .attrs = attrs, > + NULL, > +};
the same as was in 1/5
> + > +int zynqmp_pm_ggs_init(struct kobject *parent_kobj) > +{ > + return sysfs_create_group(parent_kobj, &attr_group); > +} > diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c > index 56431ad..9836174 100644 > --- a/drivers/firmware/xilinx/zynqmp.c > +++ b/drivers/firmware/xilinx/zynqmp.c > @@ -475,6 +475,10 @@ static inline int zynqmp_is_valid_ioctl(u32 ioctl_id) > case IOCTL_GET_PLL_FRAC_MODE: > case IOCTL_SET_PLL_FRAC_DATA: > case IOCTL_GET_PLL_FRAC_DATA: > + case IOCTL_WRITE_GGS: > + case IOCTL_READ_GGS: > + case IOCTL_WRITE_PGGS: > + case IOCTL_READ_PGGS: > return 1; > default: > return 0; > @@ -876,8 +880,16 @@ static int zynqmp_pm_sysfs_init(void) > if (ret) { > pr_err("%s() sysfs creation fail with error %d\n", > __func__, ret); > + goto err; > } > > + ret = zynqmp_pm_ggs_init(zynqmp_kobj); > + if (ret) { > + pr_err("%s() GGS init fail with error %d\n", > + __func__, ret); > + goto err; > + } > +err: > return ret; > } > > diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h > index 55561d0..bf6e9db 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 > @@ -98,6 +102,10 @@ enum pm_ioctl_id { > IOCTL_GET_PLL_FRAC_MODE, > IOCTL_SET_PLL_FRAC_DATA, > IOCTL_GET_PLL_FRAC_DATA, > + IOCTL_WRITE_GGS, > + IOCTL_READ_GGS, > + IOCTL_WRITE_PGGS, > + IOCTL_READ_PGGS, > }; > > enum pm_query_id { > @@ -319,6 +327,8 @@ struct zynqmp_eemi_ops { > int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1, > u32 arg2, u32 arg3, u32 *ret_payload); > > +int zynqmp_pm_ggs_init(struct kobject *parent_kobj); > + > #if IS_REACHABLE(CONFIG_ARCH_ZYNQMP) > const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); > #else >
M
|  |