From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jolly Shah Subject: [PATCH v2 3/4] drivers: firmware: xilinx: Add sysfs interface Date: Wed, 17 Jan 2018 12:20:33 -0800 Message-ID: <1516220434-22204-4-git-send-email-jollys@xilinx.com> References: <1516220434-22204-1-git-send-email-jollys@xilinx.com> Mime-Version: 1.0 Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Return-path: In-Reply-To: <1516220434-22204-1-git-send-email-jollys@xilinx.com> Sender: linux-kernel-owner@vger.kernel.org To: ard.biesheuvel@linaro.org, mingo@kernel.org, gregkh@linuxfoundation.org, matt@codeblueprint.co.uk, sudeep.holla@arm.com, hkallweit1@gmail.com, keescook@chromium.org, dmitry.torokhov@gmail.com, michal.simek@xilinx.com, robh+dt@kernel.org, mark.rutland@arm.com Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rajanv@xilinx.com, Jolly Shah List-Id: devicetree@vger.kernel.org Add Firmware-ggs sysfs interface which provides read/write interface to global storage registers. Signed-off-by: Jolly Shah Signed-off-by: Rajan Vaja --- .../ABI/stable/sysfs-driver-zynqmp-firmware | 33 +++ drivers/firmware/xilinx/zynqmp/Makefile | 2 +- drivers/firmware/xilinx/zynqmp/firmware-ggs.c | 298 +++++++++++++++++= ++++ drivers/firmware/xilinx/zynqmp/firmware.c | 26 ++ include/linux/firmware/xilinx/zynqmp/firmware.h | 2 + 5 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/stable/sysfs-driver-zynqmp-firmware create mode 100644 drivers/firmware/xilinx/zynqmp/firmware-ggs.c diff --git a/Documentation/ABI/stable/sysfs-driver-zynqmp-firmware b/Docume= ntation/ABI/stable/sysfs-driver-zynqmp-firmware new file mode 100644 index 0000000..2483215 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-driver-zynqmp-firmware @@ -0,0 +1,33 @@ +What: /sys/devices/platform/zynqmp-firmware/ggs* +Date: January 2018 +KernelVersion: 4.15.0 +Contact: "Jolly Shah" +Description: + Shows 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}. + +Users: Xilinx + +What: /sys/devices/platform/zynqmp-firmware/pggs* +Date: January 2018 +KernelVersion: 4.15.0 +Contact: "Jolly Shah" +Description: + Shows 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. +Users: Xilinx diff --git a/drivers/firmware/xilinx/zynqmp/Makefile b/drivers/firmware/xil= inx/zynqmp/Makefile index c3ec669..6629781 100644 --- a/drivers/firmware/xilinx/zynqmp/Makefile +++ b/drivers/firmware/xilinx/zynqmp/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ # Makefile for Xilinx firmwares -obj-$(CONFIG_ZYNQMP_FIRMWARE) +=3D firmware.o +obj-$(CONFIG_ZYNQMP_FIRMWARE) +=3D firmware.o firmware-ggs.o diff --git a/drivers/firmware/xilinx/zynqmp/firmware-ggs.c b/drivers/firmwa= re/xilinx/zynqmp/firmware-ggs.c new file mode 100644 index 0000000..be47ca2 --- /dev/null +++ b/drivers/firmware/xilinx/zynqmp/firmware-ggs.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Xilinx Zynq MPSoC Firmware layer + * + * Copyright (C) 2014-2018 Xilinx, Inc. + * + * Jolly Shah + * Rajan Vaja + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +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 =3D get_eemi_ops(); + + if (!eemi_ops || !eemi_ops->ioctl) + return 0; + + ret =3D eemi_ops->ioctl(0, ioctl_id, reg, 0, ret_payload); + if (ret) + return ret; + + return snprintf(buf, PAGE_SIZE, "0x%x\n", ret_payload[1]); +} + +static ssize_t write_register(const char *buf, size_t count, + u32 ioctl_id, u32 reg) +{ + char *kern_buff; + char *inbuf; + char *tok; + long mask; + long value; + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + const struct zynqmp_eemi_ops *eemi_ops =3D get_eemi_ops(); + + if (!eemi_ops || !eemi_ops->ioctl) + return -EFAULT; + + kern_buff =3D kzalloc(count, GFP_KERNEL); + if (!kern_buff) + return -ENOMEM; + + ret =3D strlcpy(kern_buff, buf, count); + if (ret < 0) { + ret =3D -EFAULT; + goto err; + } + + inbuf =3D kern_buff; + + /* Read the write mask */ + tok =3D strsep(&inbuf, " "); + if (!tok) { + ret =3D -EFAULT; + goto err; + } + + ret =3D kstrtol(tok, 16, &mask); + if (ret) { + ret =3D -EFAULT; + goto err; + } + + /* Read the write value */ + tok =3D strsep(&inbuf, " "); + if (!tok) { + ret =3D -EFAULT; + goto err; + } + + ret =3D kstrtol(tok, 16, &value); + if (ret) { + ret =3D -EFAULT; + goto err; + } + + ret =3D eemi_ops->ioctl(0, ioctl_id, reg, 0, ret_payload); + if (ret) { + ret =3D -EFAULT; + goto err; + } + ret_payload[1] &=3D ~mask; + value &=3D mask; + value |=3D ret_payload[1]; + + ret =3D eemi_ops->ioctl(0, ioctl_id, reg, value, NULL); + if (ret) + ret =3D -EFAULT; + +err: + kfree(kern_buff); + if (ret) + return ret; + + return count; +} + +/** + * ggs_show - Show global general storage (ggs) sysfs attribute + * @dev: 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/ggs0 + */ +static ssize_t ggs_show(struct device *dev, + struct device_attribute *attr, + char *buf, + u32 reg) +{ + return read_register(buf, IOCTL_READ_GGS, reg); +} + +/** + * ggs_store - Store global general storage (ggs) sysfs attribute + * @dev: 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/ggs0 + */ +static ssize_t ggs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count, + u32 reg) +{ + if (!dev || !attr || !buf || !count || reg >=3D GSS_NUM_REGS) + return -EINVAL; + + return write_register(buf, count, IOCTL_WRITE_GGS, reg); +} + +/* GGS register show functions */ +#define GGS0_SHOW(N) \ + ssize_t ggs##N##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + return ggs_show(dev, 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 *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t count) \ + { \ + return ggs_store(dev, attr, buf, count, N); \ + } + +static GGS0_STORE(0); +static GGS0_STORE(1); +static GGS0_STORE(2); +static GGS0_STORE(3); + +/* GGS register device attributes */ +static DEVICE_ATTR_RW(ggs0); +static DEVICE_ATTR_RW(ggs1); +static DEVICE_ATTR_RW(ggs2); +static DEVICE_ATTR_RW(ggs3); + +#define CREATE_GGS_DEVICE(dev, N) \ +do { \ + if (device_create_file(dev, &dev_attr_ggs##N)) \ + dev_err(dev, "unable to create ggs%d attribute\n", N); \ +} while (0) + +/** + * pggs_show - Show persistent global general storage (pggs) sysfs attribu= te + * @dev: 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 *dev, + struct device_attribute *attr, + char *buf, + u32 reg) +{ + return read_register(buf, IOCTL_READ_GGS, reg); +} + +/** + * pggs_store - Store persistent global general storage (pggs) sysfs attri= bute + * @dev: 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 *dev, + struct device_attribute *attr, + const char *buf, + size_t count, + u32 reg) +{ + return write_register(buf, count, IOCTL_WRITE_PGGS, reg); +} + +#define PGGS0_SHOW(N) \ + ssize_t pggs##N##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + return pggs_show(dev, attr, buf, N); \ + } + +/* PGGS register show functions */ +static PGGS0_SHOW(0); +static PGGS0_SHOW(1); +static PGGS0_SHOW(2); +static PGGS0_SHOW(3); + +#define PGGS0_STORE(N) \ + ssize_t pggs##N##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t count) \ + { \ + return pggs_store(dev, attr, buf, count, N); \ + } + +/* PGGS register store functions */ +static PGGS0_STORE(0); +static PGGS0_STORE(1); +static PGGS0_STORE(2); +static PGGS0_STORE(3); + +/* PGGS register device attributes */ +static DEVICE_ATTR_RW(pggs0); +static DEVICE_ATTR_RW(pggs1); +static DEVICE_ATTR_RW(pggs2); +static DEVICE_ATTR_RW(pggs3); + +#define CREATE_PGGS_DEVICE(dev, N) \ +do { \ + if (device_create_file(dev, &dev_attr_pggs##N)) \ + dev_err(dev, "unable to create pggs%d attribute\n", N); \ +} while (0) + +void zynqmp_pm_ggs_init(struct device *dev) +{ + /* Create Global General Storage register. */ + CREATE_GGS_DEVICE(dev, 0); + CREATE_GGS_DEVICE(dev, 1); + CREATE_GGS_DEVICE(dev, 2); + CREATE_GGS_DEVICE(dev, 3); + + /* Create Persistent Global General Storage register. */ + CREATE_PGGS_DEVICE(dev, 0); + CREATE_PGGS_DEVICE(dev, 1); + CREATE_PGGS_DEVICE(dev, 2); + CREATE_PGGS_DEVICE(dev, 3); +} diff --git a/drivers/firmware/xilinx/zynqmp/firmware.c b/drivers/firmware/x= ilinx/zynqmp/firmware.c index 8ebacb6..c42cf9f 100644 --- a/drivers/firmware/xilinx/zynqmp/firmware.c +++ b/drivers/firmware/xilinx/zynqmp/firmware.c @@ -15,11 +15,14 @@ #include #include #include +#include #include #include #include +#define DRIVER_NAME "zynqmp_firmware" + /** * zynqmp_pm_ret_code - Convert PMU-FW error codes to Linux error codes * @ret_status: PMUFW return code @@ -984,4 +987,27 @@ static int __init zynqmp_plat_init(void) return ret; } +static const struct of_device_id firmware_of_match[] =3D { + { .compatible =3D "xlnx,zynqmp-firmware", }, + { /* end of table */ }, +}; + +MODULE_DEVICE_TABLE(of, firmware_of_match); + +static int zynqmp_firmware_probe(struct platform_device *pdev) +{ + zynqmp_pm_ggs_init(&pdev->dev); + + return 0; +} + +static struct platform_driver zynqmp_firmware_platform_driver =3D { + .probe =3D zynqmp_firmware_probe, + .driver =3D { + .name =3D DRIVER_NAME, + .of_match_table =3D firmware_of_match, + }, +}; +builtin_platform_driver(zynqmp_firmware_platform_driver); + early_initcall(zynqmp_plat_init); diff --git a/include/linux/firmware/xilinx/zynqmp/firmware.h b/include/linu= x/firmware/xilinx/zynqmp/firmware.h index 42d6e74..97b3b7b 100644 --- a/include/linux/firmware/xilinx/zynqmp/firmware.h +++ b/include/linux/firmware/xilinx/zynqmp/firmware.h @@ -561,6 +561,8 @@ int invoke_pm_fn(u32 pm_api_id, u32 arg0, u32 arg1, u32= arg2, u32 arg3, u32 *ret_payload); int zynqmp_pm_ret_code(u32 ret_status); +void zynqmp_pm_ggs_init(struct device *dev); + #if IS_REACHABLE(CONFIG_ARCH_ZYNQMP) const struct zynqmp_eemi_ops *get_eemi_ops(void); #else -- 2.7.4 This email and any attachments are intended for the sole use of the named r= ecipient(s) and contain(s) confidential information that may be proprietary= , privileged or copyrighted under applicable law. If you are not the intend= ed recipient, do not read, copy, or forward this email message or any attac= hments. Delete this email message and any attachments immediately.