public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Michal Simek <michal.simek@amd.com>
To: Ronak Jain <ronak.jain@amd.com>, senthilnathan.thangaraj@amd.com
Cc: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH 2/2] firmware: zynqmp: Add dynamic CSU register discovery and sysfs interface
Date: Thu, 23 Apr 2026 13:34:14 +0200	[thread overview]
Message-ID: <ec571a88-c3f7-40a2-9671-5e2defe9966d@amd.com> (raw)
In-Reply-To: <20260408114244.2852015-3-ronak.jain@amd.com>



On 4/8/26 13:42, Ronak Jain wrote:
> Add support for dynamically discovering and exposing Configuration
> Security Unit (CSU) registers through sysfs. Leverage the existing
> PM_QUERY_DATA API to discover available registers at runtime, making
> the interface flexible and maintainable.
> 
> Key features:
> - Dynamic register discovery using PM_QUERY_DATA API
>    * PM_QID_GET_NODE_COUNT: Query number of available registers
>    * PM_QID_GET_NODE_NAME: Query register names by index
> - Automatic sysfs attribute creation under csu_registers/ group
> - Read operations via existing IOCTL_READ_REG API
> - Write operations via existing IOCTL_MASK_WRITE_REG API
> - Firmware-enforced access control (read-only registers reject writes)
> 
> The sysfs interface is created at:
>    /sys/devices/platform/firmware:zynqmp-firmware/csu_registers/
> 
> Currently supported registers include:
>    - multiboot (CSU_MULTI_BOOT)
>    - idcode (CSU_IDCODE, read-only)
>    - pcap-status (CSU_PCAP_STATUS, read-only)
> 
> The dynamic discovery approach allows firmware to control which
> registers are exposed without requiring kernel changes, improving
> maintainability and security.
> 
> Signed-off-by: Ronak Jain <ronak.jain@amd.com>
> ---
>   MAINTAINERS                              |  10 +
>   drivers/firmware/xilinx/Makefile         |   2 +-
>   drivers/firmware/xilinx/zynqmp-csu-reg.c | 249 +++++++++++++++++++++++
>   drivers/firmware/xilinx/zynqmp-csu-reg.h |  18 ++
>   drivers/firmware/xilinx/zynqmp.c         |   6 +
>   include/linux/firmware/xlnx-zynqmp.h     |   4 +-
>   6 files changed, 287 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/firmware/xilinx/zynqmp-csu-reg.c
>   create mode 100644 drivers/firmware/xilinx/zynqmp-csu-reg.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 10d12b51b1f6..37fe2b7e0ccf 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -29212,6 +29212,16 @@ F:	drivers/dma/xilinx/xdma.c
>   F:	include/linux/dma/amd_xdma.h
>   F:	include/linux/platform_data/amd_xdma.h
>   
> +XILINX ZYNQMP CSU REGISTER DRIVER
> +M:	Senthil Nathan Thangaraj <senthilnathan.thangaraj@amd.com>
> +R:	Michal Simek <michal.simek@amd.com>
> +R:	Ronak Jain <ronak.jain@amd.com>
> +L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
> +S:	Maintained
> +F:	Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
> +F:	drivers/firmware/xilinx/zynqmp-csu-reg.c
> +F:	drivers/firmware/xilinx/zynqmp-csu-reg.h
> +
>   XILINX ZYNQMP DPDMA DRIVER
>   M:	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>   L:	dmaengine@vger.kernel.org
> diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
> index 8db0e66b6b7e..6203f41daaa6 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 zynqmp-ufs.o zynqmp-crypto.o
> +obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o zynqmp-ufs.o zynqmp-crypto.o zynqmp-csu-reg.o
>   obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o
> diff --git a/drivers/firmware/xilinx/zynqmp-csu-reg.c b/drivers/firmware/xilinx/zynqmp-csu-reg.c
> new file mode 100644
> index 000000000000..1f304ce858b1
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp-csu-reg.c
> @@ -0,0 +1,249 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Xilinx Zynq MPSoC CSU Register Access
> + *
> + * Copyright (C) 2026 Advanced Micro Devices, Inc.
> + *
> + *  Michal Simek <michal.simek@amd.com>
> + *  Ronak Jain <ronak.jain@amd.com>
> + */
> +
> +#include <linux/firmware/xlnx-zynqmp.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#include "zynqmp-csu-reg.h"
> +
> +/* Node ID for CSU module in firmware */
> +#define CSU_NODE_ID 0
> +
> +/* Maximum number of CSU registers supported */
> +#define MAX_CSU_REGS 50
> +
> +/* Size of register name returned by firmware (3 u32 words = 12 bytes) */
> +#define CSU_REG_NAME_LEN 12
> +
> +/**
> + * struct zynqmp_csu_reg - CSU register information
> + * @id: Register index from firmware
> + * @name: Register name
> + * @attr: Device attribute for sysfs
> + */
> +struct zynqmp_csu_reg {
> +	u32 id;
> +	char name[CSU_REG_NAME_LEN];
> +	struct device_attribute attr;
> +};
> +
> +/**
> + * struct zynqmp_csu_data - Per-device CSU data
> + * @csu_regs: Array of CSU registers
> + * @csu_reg_count: Number of CSU registers
> + * @csu_attr_group: Attribute group for sysfs
> + */
> +struct zynqmp_csu_data {
> +	struct zynqmp_csu_reg *csu_regs;
> +	int csu_reg_count;
> +	struct attribute_group csu_attr_group;
> +};
> +
> +/**
> + * zynqmp_pm_get_node_count() - Get number of supported nodes via QUERY_DATA
> + *
> + * Return: Number of nodes on success, or negative error code
> + */
> +static int zynqmp_pm_get_node_count(void)
> +{
> +	struct zynqmp_pm_query_data qdata = {0};
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +	int ret;
> +
> +	qdata.qid = PM_QID_GET_NODE_COUNT;
> +
> +	ret = zynqmp_pm_query_data(qdata, ret_payload);
> +	if (ret)
> +		return ret;
> +
> +	return ret_payload[1];
> +}
> +
> +/**
> + * zynqmp_pm_get_node_name() - Get node name via QUERY_DATA
> + * @index: Register index
> + * @name: Buffer to store register name
> + *
> + * Return: 0 on success, error code otherwise
> + */
> +static int zynqmp_pm_get_node_name(u32 index, char *name)
> +{
> +	struct zynqmp_pm_query_data qdata = {0};
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +	int ret;
> +
> +	qdata.qid = PM_QID_GET_NODE_NAME;
> +	qdata.arg1 = index;
> +
> +	ret = zynqmp_pm_query_data(qdata, ret_payload);
> +	if (ret)
> +		return ret;
> +
> +	memcpy(name, &ret_payload[1], CSU_REG_NAME_LEN);
> +	name[CSU_REG_NAME_LEN - 1] = '\0';
> +
> +	return 0;
> +}
> +
> +/**
> + * zynqmp_csu_reg_show() - Generic show function for all registers
> + * @dev: Device pointer
> + * @attr: Device attribute
> + * @buf: Output buffer
> + *
> + * Return: Number of bytes written to buffer, or error code
> + */
> +static ssize_t zynqmp_csu_reg_show(struct device *dev,
> +				   struct device_attribute *attr,
> +				   char *buf)
> +{
> +	struct zynqmp_csu_reg *reg;
> +	u32 value;
> +	int ret;
> +
> +	/* Use container_of to get register directly */
> +	reg = container_of(attr, struct zynqmp_csu_reg, attr);
> +
> +	ret = zynqmp_pm_sec_read_reg(CSU_NODE_ID, reg->id, &value);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "0x%08x\n", value);
> +}
> +
> +/**
> + * zynqmp_csu_reg_store() - Generic store function for writable registers
> + * @dev: Device pointer
> + * @attr: Device attribute
> + * @buf: Input buffer
> + * @count: Buffer size
> + *
> + * Format: "mask value" - both mask and value required
> + * Example: echo "0xFFFFFFFF 0x12345678" > register
> + *
> + * Return: count on success, error code otherwise
> + */
> +static ssize_t zynqmp_csu_reg_store(struct device *dev,
> +				    struct device_attribute *attr,
> +				    const char *buf, size_t count)
> +{
> +	struct zynqmp_csu_reg *reg;
> +	u32 mask, value;
> +	int ret;
> +
> +	reg = container_of(attr, struct zynqmp_csu_reg, attr);
> +
> +	if (sscanf(buf, "%x %x", &mask, &value) != 2)
> +		return -EINVAL;
> +
> +	ret = zynqmp_pm_sec_mask_write_reg(CSU_NODE_ID, reg->id, mask, value);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +/**
> + * zynqmp_csu_discover_registers() - Discover CSU registers from firmware
> + * @pdev: Platform device pointer
> + *
> + * This function uses PM_QUERY_DATA to discover all available CSU registers
> + * and creates sysfs group under /sys/devices/platform/firmware:zynqmp-firmware/
> + *
> + * Return: 0 on success, error code otherwise
> + */
> +int zynqmp_csu_discover_registers(struct platform_device *pdev)
> +{
> +	struct zynqmp_csu_data *csu_data;
> +	struct attribute **attrs;
> +	int count, ret, i;
> +
> +	ret = zynqmp_pm_is_function_supported(PM_QUERY_DATA, PM_QID_GET_NODE_COUNT);
> +	if (ret) {
> +		dev_dbg(&pdev->dev, "CSU register discovery not supported by current firmware\n");
> +		return 0;
> +	}
> +
> +	count = zynqmp_pm_get_node_count();
> +	if (count < 0)
> +		return count;
> +	if (count == 0) {
> +		dev_dbg(&pdev->dev, "No nodes available from firmware\n");
> +		return 0;
> +	}
> +
> +	/* Validate count to prevent excessive memory allocation */
> +	if (count > MAX_CSU_REGS) {
> +		dev_err(&pdev->dev, "Register count %d exceeds maximum %d\n",
> +			count, MAX_CSU_REGS);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(&pdev->dev, "Discovered %d nodes from firmware\n", count);
> +
> +	csu_data = devm_kzalloc(&pdev->dev, sizeof(*csu_data), GFP_KERNEL);
> +	if (!csu_data)
> +		return -ENOMEM;
> +
> +	csu_data->csu_reg_count = count;

Where is this used?

> +
> +	csu_data->csu_regs = devm_kcalloc(&pdev->dev, count, sizeof(*csu_data->csu_regs),
> +					  GFP_KERNEL);
> +	if (!csu_data->csu_regs) {
> +		devm_kfree(&pdev->dev, csu_data);

My bet is that you are freeing this memory because csu is optional and you don't 
want to waste memory. Is this correct assumption?
If yes, I think it will be good if you can express this intention in commit 
message.


> +		return -ENOMEM;
> +	}
> +
> +	attrs = devm_kcalloc(&pdev->dev, count + 1, sizeof(*attrs), GFP_KERNEL);
> +	if (!attrs) {
> +		devm_kfree(&pdev->dev, csu_data->csu_regs);
> +		devm_kfree(&pdev->dev, csu_data);
> +		return -ENOMEM;
> +	}
> +
> +	for (i = 0; i < count; i++) {
> +		struct zynqmp_csu_reg *reg = &csu_data->csu_regs[i];
> +		struct device_attribute *dev_attr = &reg->attr;
> +
> +		reg->id = i;
> +
> +		ret = zynqmp_pm_get_node_name(i, reg->name);
> +		if (ret) {
> +			dev_warn(&pdev->dev, "Failed to get name for register %d\n", i);
> +			snprintf(reg->name, sizeof(reg->name), "csu_reg_%d", i);
> +		}
> +
> +		/* Create sysfs attribute - firmware enforces actual access control */
> +		sysfs_attr_init(&dev_attr->attr);
> +		dev_attr->attr.name = reg->name;
> +		dev_attr->attr.mode = 0644;

You should comment this a little bit. In commit message you are saying that some 
of that registers are read only but here you are exposing all of them as rw.

Do you have any interface/attribute/flag where you can find out if this is read 
only?

I expect if you try to write to read only firmware driver will just reject it 
anyway. If you don't have any flag for it, it would be good to describe this 
limiation in commit message.


> +		dev_attr->show = zynqmp_csu_reg_show;
> +		dev_attr->store = zynqmp_csu_reg_store;
> +
> +		attrs[i] = &dev_attr->attr;
> +
> +		dev_dbg(&pdev->dev, "Register %d: id=%d name=%s\n", i, reg->id, reg->name);
> +	}
> +
> +	csu_data->csu_attr_group.name = "csu_registers";
> +	csu_data->csu_attr_group.attrs = attrs;
> +
> +	ret = devm_device_add_group(&pdev->dev, &csu_data->csu_attr_group);
> +	if (ret) {
> +		devm_kfree(&pdev->dev, attrs);
> +		devm_kfree(&pdev->dev, csu_data->csu_regs);
> +		devm_kfree(&pdev->dev, csu_data);
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_csu_discover_registers);
> diff --git a/drivers/firmware/xilinx/zynqmp-csu-reg.h b/drivers/firmware/xilinx/zynqmp-csu-reg.h
> new file mode 100644
> index 000000000000..b12415db3496
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp-csu-reg.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx Zynq MPSoC CSU Register Access
> + *
> + * Copyright (C) 2026 Advanced Micro Devices, Inc.
> + *
> + *  Michal Simek <michal.simek@amd.com>
> + *  Ronak Jain <ronak.jain@amd.com>
> + */
> +
> +#ifndef __ZYNQMP_CSU_REG_H__
> +#define __ZYNQMP_CSU_REG_H__
> +
> +#include <linux/platform_device.h>
> +
> +int zynqmp_csu_discover_registers(struct platform_device *pdev);
> +
> +#endif /* __ZYNQMP_CSU_REG_H__ */
> diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
> index fbe8510f4927..b549d07f7497 100644
> --- a/drivers/firmware/xilinx/zynqmp.c
> +++ b/drivers/firmware/xilinx/zynqmp.c
> @@ -27,6 +27,7 @@
>   
>   #include <linux/firmware/xlnx-zynqmp.h>
>   #include <linux/firmware/xlnx-event-manager.h>
> +#include "zynqmp-csu-reg.h"
>   #include "zynqmp-debug.h"
>   
>   /* Max HashMap Order for PM API feature check (1<<7 = 128) */
> @@ -2120,6 +2121,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
>   			dev_err_probe(&pdev->dev, PTR_ERR(em_dev), "EM register fail with error\n");
>   	}
>   
> +	/* Discover CSU registers dynamically */
> +	ret = zynqmp_csu_discover_registers(pdev);
> +	if (ret)
> +		dev_warn(&pdev->dev, "CSU register discovery failed: %d\n", ret);
> +
>   	return of_platform_populate(dev->of_node, NULL, NULL, dev);
>   }
>   
> diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
> index d70dcd462b44..a4b293eb96ce 100644
> --- a/include/linux/firmware/xlnx-zynqmp.h
> +++ b/include/linux/firmware/xlnx-zynqmp.h
> @@ -3,7 +3,7 @@
>    * Xilinx Zynq MPSoC Firmware layer
>    *
>    *  Copyright (C) 2014-2021 Xilinx
> - *  Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc.
> + *  Copyright (C) 2022 - 2026 Advanced Micro Devices, Inc.
>    *
>    *  Michal Simek <michal.simek@amd.com>
>    *  Davorin Mista <davorin.mista@aggios.com>
> @@ -262,6 +262,8 @@ enum pm_query_id {
>   	PM_QID_CLOCK_GET_NUM_CLOCKS = 12,
>   	PM_QID_CLOCK_GET_MAX_DIVISOR = 13,
>   	PM_QID_PINCTRL_GET_ATTRIBUTES = 15,
> +	PM_QID_GET_NODE_NAME = 16,
> +	PM_QID_GET_NODE_COUNT = 17,
>   };
>   
>   enum rpu_oper_mode {

Thanks,
Michal

      reply	other threads:[~2026-04-23 11:34 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-08 11:42 [PATCH 0/2] Add dynamic CSU register sysfs interface Ronak Jain
2026-04-08 11:42 ` [PATCH 1/2] Documentation: ABI: add sysfs interface for ZynqMP CSU registers Ronak Jain
2026-04-08 11:42 ` [PATCH 2/2] firmware: zynqmp: Add dynamic CSU register discovery and sysfs interface Ronak Jain
2026-04-23 11:34   ` Michal Simek [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ec571a88-c3f7-40a2-9671-5e2defe9966d@amd.com \
    --to=michal.simek@amd.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ronak.jain@amd.com \
    --cc=senthilnathan.thangaraj@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox