* Re: [PATCH] fpga: xilinx-pr-decoupler: Use devm_clk_get_prepared()
From: Xu Yilun @ 2026-06-26 17:22 UTC (permalink / raw)
To: Pandey, Radhey Shyam
Cc: Michal Simek, linux-kernel, monstr, git, Moritz Fischer, Tom Rix,
Xu Yilun, moderated list:ARM/ZYNQ ARCHITECTURE,
open list:FPGA MANAGER FRAMEWORK
In-Reply-To: <5bd0f7db-7ec0-4390-b6ea-1edca46e1ca7@amd.com>
On Tue, Jun 23, 2026 at 04:31:28PM +0530, Pandey, Radhey Shyam wrote:
> > The driver keeps the "aclk" clock prepared but disabled in its idle
> > state, toggling only the atomic clk_enable()/clk_disable() around
> > register accesses in the bridge enable_set/enable_show callbacks.
> >
> > At probe time this was open-coded as clk_prepare_enable() immediately
> > followed by clk_disable(), leaving the clock prepared, with a matching
> > clk_unprepare() in the error path and in remove().
> >
> > devm_clk_get_prepared() expresses exactly this: it gets and prepares the
> > clock and unprepares it automatically on driver detach.
> >
> > Use it to drop the manual prepare/disable dance, the error-path
> > unprepare, and the now-empty clock teardown in remove().
> >
> > Signed-off-by: Michal Simek <michal.simek@amd.com>
>
> Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Reviewed-by: Xu Yilun <yilun.xu@intel.com>
Will apply to for-next when 7.2-rc1 comes.
^ permalink raw reply
* Re: [PATCH v3] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
From: Xu Yilun @ 2026-06-26 17:11 UTC (permalink / raw)
To: tze.yee.ng
Cc: Moritz Fischer, Xu Yilun, Tom Rix, Greg Kroah-Hartman,
Richard Gong, Alan Tull, linux-fpga, linux-kernel, tien.sung.ang
In-Reply-To: <0119f69b467e2b90240ebaa3aee3120f621ddee3.1782295811.git.tze.yee.ng@altera.com>
> @@ -195,20 +195,20 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
> ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
> &ctype, sizeof(ctype));
> if (ret < 0)
> - goto init_done;
> + goto init_error;
>
> ret = wait_for_completion_timeout(
> &priv->status_return_completion, S10_RECONFIG_TIMEOUT);
> if (!ret) {
> dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
> ret = -ETIMEDOUT;
> - goto init_done;
> + goto init_error;
> }
>
> ret = 0;
Why re-initialize ret here?
> if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
> ret = -ETIMEDOUT;
> - goto init_done;
> + goto init_error;
> }
>
> /* Allocate buffers from the service layer's pool. */
> @@ -216,16 +216,19 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
> kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
> if (IS_ERR(kbuf)) {
> s10_free_buffers(mgr);
> - ret = PTR_ERR(kbuf);
> - goto init_done;
> + ret = -ENOMEM;
Why change the ret?
> + goto init_error;
> }
>
> priv->svc_bufs[i].buf = kbuf;
> priv->svc_bufs[i].lock = 0;
> }
>
> -init_done:
> + goto init_done;
Happy path? why not just return 0; And you don't need init_done at all.
> +
> +init_error:
> stratix10_svc_done(priv->chan);
> +init_done:
> return ret;
> }
^ permalink raw reply
* Re: [PATCH] fpga: altera-cvp: Add Agilex5 CVP credit register support
From: Xu Yilun @ 2026-06-26 16:37 UTC (permalink / raw)
To: muhammad.nazim.amirul.nazle.asmade
Cc: mdf, yilun.xu, trix, linux-fpga, linux-kernel
In-Reply-To: <20260619034327.26966-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
On Thu, Jun 18, 2026 at 08:43:27PM -0700, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>
> Extend the CvP driver to support Agilex5, which uses a different
> register layout than previous SoC FPGA variants for CVP bitstream
> transfer.
>
> Agilex5 uses a different VSEC layout (VSEC length 0x60) and a
> dedicated CVP credit register at offset 0x5C with 12-bit width,
> compared to offset 0x49 with 8-bit width on other platforms.
>
> Detect the device family at probe time by reading the VSEC specific
> header and checking the VSEC length field. Introduce
> device_family_type to distinguish V1, V2, and V2 Agilex5 variants.
> Use altera_read_config_dword() with a 0xFFF mask for Agilex5 credits
> and altera_read_config_byte() for all other devices.
> Fix the credit
> comparison by removing the incorrect u8 cast on sent_packets.
Is it a bug fix for existing cases? If yes, split it out to a separate
patch. Otherwise, don't say 'fix'.
>
> Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
> ---
> drivers/fpga/altera-cvp.c | 48 +++++++++++++++++++++++++++++++++++----
> 1 file changed, 43 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> index 44badfd11e1b..58128de2a8c5 100644
> --- a/drivers/fpga/altera-cvp.c
> +++ b/drivers/fpga/altera-cvp.c
> @@ -10,6 +10,7 @@
> * Firmware must be in binary "rbf" format.
> */
>
> +#include <linux/bitfield.h>
> #include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/fpga/fpga-mgr.h>
> @@ -22,6 +23,9 @@
> #define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */
>
> /* Vendor Specific Extended Capability Registers */
> +#define VSE_PCIE_SPECIFIC_HEADER 0x4 /* VSEC ID, Revision, length */
> +#define VSEC_LENGTH GENMASK(31, 20)
Are they PCIe standard registers?
> +#define AGILEX5_VSEC_LENGTH 0x60 /* Agilex5 only */
> #define VSE_CVP_STATUS 0x1c /* 32bit */
> #define VSE_CVP_STATUS_CFG_RDY BIT(18) /* CVP_CONFIG_READY */
> #define VSE_CVP_STATUS_CFG_ERR BIT(19) /* CVP_CONFIG_ERROR */
> @@ -48,6 +52,7 @@
> #define V1_VSEC_OFFSET 0x200 /* Vendor Specific Offset V1 */
> /* V2 Defines */
> #define VSE_CVP_TX_CREDITS 0x49 /* 8bit */
> +#define VSE_CVP_AG5_TX_CREDITS 0x5C /* 12bit credits for Agilex5 */
>
> #define V2_CREDIT_TIMEOUT_US 40000
> #define V2_CHECK_CREDIT_US 10
> @@ -58,6 +63,9 @@
>
> #define DRV_NAME "altera-cvp"
> #define ALTERA_CVP_MGR_NAME "Altera CvP FPGA Manager"
> +#define SOCFPGA_CVP_V1_OTHERS 0x1
> +#define SOCFPGA_CVP_V2_OTHERS 0x2
I didn't see they are used anywhere.
> +#define SOCFPGA_CVP_V2_AGILEX5 0x3
>
> /* Write block sizes */
> #define ALTERA_CVP_V1_SIZE 4
> @@ -78,6 +86,7 @@ struct altera_cvp_conf {
> u32 sent_packets;
> u32 vsec_offset;
> const struct cvp_priv *priv;
> + u32 device_family_type;
> };
>
> struct cvp_priv {
> @@ -228,18 +237,32 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr,
> u32 timeout = V2_CREDIT_TIMEOUT_US / V2_CHECK_CREDIT_US;
> struct altera_cvp_conf *conf = mgr->priv;
> int ret;
> - u8 val;
> + u32 val;
> + u32 credit_mask = GENMASK(7, 0);
> + u32 vse_cvp_tx_credits_offset = VSE_CVP_TX_CREDITS;
I'm not strict on reverse xmas tree, but don't make a complete mess.
> +
> + if (conf->device_family_type == SOCFPGA_CVP_V2_AGILEX5) {
> + vse_cvp_tx_credits_offset = VSE_CVP_AG5_TX_CREDITS;
> + credit_mask = GENMASK(11, 0);
> + }
>
> do {
> - ret = altera_read_config_byte(conf, VSE_CVP_TX_CREDITS, &val);
> + /* READ DWORD is required for Agilex5 but READ BYTE is required for non-Agilex5 */
> + if (conf->device_family_type == SOCFPGA_CVP_V2_AGILEX5)
> + ret = altera_read_config_dword(conf, vse_cvp_tx_credits_offset, &val);
> + else
> + ret = altera_read_config_byte(conf, vse_cvp_tx_credits_offset, (u8 *)&val);
> +
> if (ret) {
> dev_err(&conf->pci_dev->dev,
> "Error reading CVP Credit Register\n");
> return ret;
> }
>
> + val = val & credit_mask;
Don't you have struct cvp_priv to describe HW differences? Why not use
it? Why Bifurcating code like this?
> +
> /* Return if there is space in FIFO */
> - if (val - (u8)conf->sent_packets)
> + if (val - conf->sent_packets)
> return 0;
>
> ret = altera_cvp_chk_error(mgr, blocks * ALTERA_CVP_V2_SIZE);
> @@ -507,6 +530,8 @@ static int altera_cvp_write_complete(struct fpga_manager *mgr,
> conf->priv->user_time_us);
> if (ret)
> dev_err(&mgr->dev, "PLD_CLK_IN_USE|USERMODE timeout\n");
> + else
> + dev_notice(&mgr->dev, "CVP write completed successfully.\n");
Why adding this print, anything to do with "support Agilex5"?
>
> return ret;
> }
> @@ -627,10 +652,23 @@ static int altera_cvp_probe(struct pci_dev *pdev,
> conf->pci_dev = pdev;
> conf->write_data = altera_cvp_write_data_iomem;
>
> - if (conf->vsec_offset == V1_VSEC_OFFSET)
> + /* To differentiate the target SOCFPGA */
> + if (conf->vsec_offset == V1_VSEC_OFFSET) {
> conf->priv = &cvp_priv_v1;
> - else
> + conf->device_family_type = SOCFPGA_CVP_V1_OTHERS;
Why add another board type indicator, conf->priv won't work?
> + dev_notice(&pdev->dev, "V1 target SOCFPGA detected.\n");
No news is good news, a good driver keeps quiet.
> + } else {
> + /* Agilex7, Stratix10, Agilex5*/
> conf->priv = &cvp_priv_v2;
> + pci_read_config_dword(pdev, offset + VSE_PCIE_SPECIFIC_HEADER, ®val);
> + if (FIELD_GET(VSEC_LENGTH, regval) == AGILEX5_VSEC_LENGTH) {
> + conf->device_family_type = SOCFPGA_CVP_V2_AGILEX5;
> + dev_notice(&pdev->dev, "V2 target SOCFPGA Agilex5 detected.\n");
> + } else {
> + conf->device_family_type = SOCFPGA_CVP_V2_OTHERS;
> + dev_notice(&pdev->dev, "V2 target SOCFPGA detected.\n");
> + }
> + }
BTW: I really suggest you don't send multiple threads at a time. Getting the
first patch right, then I'll have confidence to move to the next.
>
> conf->map = pci_iomap(pdev, CVP_BAR, 0);
> if (!conf->map) {
> --
> 2.43.7
>
>
^ permalink raw reply
* Re: [PATCH] fpga: altera-cvp: Retry teardown and reset CVP state on failure
From: Xu Yilun @ 2026-06-26 14:46 UTC (permalink / raw)
To: muhammad.nazim.amirul.nazle.asmade
Cc: mdf, yilun.xu, trix, linux-fpga, linux-kernel
In-Reply-To: <20260618112410.303-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
On Thu, Jun 18, 2026 at 04:24:10AM -0700, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>
> If an incorrect bitstream is sent, the teardown may fail due to a
> CFG_RDY timeout. When this happens, reset CVP_MODE and HIP_CLK_SEL
Please don't just tell the register name, tell us what is the hardware
mechanism in nature English.
> bits to clean up the hardware state and return -EAGAIN, allowing the
Please help me understand why the teardown fails and why a clean up save
the world.
> caller to retry. Introduce altera_cvp_recovery() to wrap this retry
> logic with a maximum of CVP_TEARDOWN_MAX_RETRY attempts.
Why should we retry multiple times?
>
> Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
> ---
> drivers/fpga/altera-cvp.c | 36 +++++++++++++++++++++++++++++++++---
> 1 file changed, 33 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> index 44badfd11e1b..29faf6f5bde1 100644
> --- a/drivers/fpga/altera-cvp.c
> +++ b/drivers/fpga/altera-cvp.c
> @@ -63,6 +63,8 @@
> #define ALTERA_CVP_V1_SIZE 4
> #define ALTERA_CVP_V2_SIZE 4096
>
> +/* Tear-down retry */
> +#define CVP_TEARDOWN_MAX_RETRY 10
> /* Optional CvP config error status check for debugging */
> static bool altera_cvp_chkcfg;
>
> @@ -305,12 +307,40 @@ static int altera_cvp_teardown(struct fpga_manager *mgr,
> /* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */
> ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0,
> conf->priv->poll_time_us);
> - if (ret)
> + if (ret) {
> dev_err(&mgr->dev, "CFG_RDY == 0 timeout\n");
> + goto error_path;
> + }
>
> return ret;
> +
> +error_path:
> + /* reset CVP_MODE and HIP_CLK_SEL bit */
> + altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
> + val &= ~VSE_CVP_MODE_CTRL_HIP_CLK_SEL;
> + val &= ~VSE_CVP_MODE_CTRL_CVP_MODE;
> + altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
Put the reset in your recovery loop.
And if the code block does a meaningful job, don't copy and paste it
everywhere, make a helper.
> +
> + return -EAGAIN;
> +
> }
>
> +static int altera_cvp_recovery(struct fpga_manager *mgr,
> + struct fpga_image_info *info)
> +{
> + int ret = 0, retry = 0;
Try not to initialize local variables that will always be overwritten
later.
> +
> + for (retry = 0; retry < CVP_TEARDOWN_MAX_RETRY; retry++) {
> + ret = altera_cvp_teardown(mgr, info);
> + if (!ret)
> + break;
Nothing to do on success, just return 0;
> + dev_warn(&mgr->dev,
> + "%s: [%d] Tear-down failed. Retrying\n",
> + __func__,
> + retry);
You do dev_err() in altera_cvp_teardown(), does the warn proper print
level?
And not sure if the 10-times retry is expected or not, if yes, you
really don't have to yell out again and again. If not, please find
decent solution.
^ permalink raw reply
* Re: [RFC PATCH] fpga: region: Add support for FPGA region variants
From: Xu Yilun @ 2026-06-26 12:58 UTC (permalink / raw)
To: Marco Pagani; +Cc: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
In-Reply-To: <20260608164247.1998417-1-marco.pagani@linux.dev>
On Mon, Jun 08, 2026 at 06:42:45PM +0200, Marco Pagani wrote:
> This RFC proposes a proof-of-concept implementation of FPGA region
> variants, a mechanism that introduces a common way to handle
> dynamic partial reconfiguration from userspace. The proposed approach
There are many threads talking about userspace reconfiguration, the
latest one is this:
https://lore.kernel.org/linux-fpga/20250519033950.2669858-1-nava.kishore.manne@amd.com/
Before we dive into detail, could you help tell why the previous one
won't work so we must switch to the new interface.
> is safe and aligned with the mainline kernel's stance on hardware
> management by constraining the hardware to a mutually exclusive set
> of configurations (variants) defined upfront. This is a realistic
> assumption for FPGAs, as regions are typically statically defined and
> synthesized during the system design phase. To keep the architecture
> realistic, the following additional constraints are introduced:
> (i) variants cannot be nested, and (ii) variants cannot contain FPGA
> bridges.
>
> The interface and core logic for the variant mechanism are defined
> in the fpga-region and implemented in a backwards-compatible way.
> The fpga-region now optionally exports sysfs attributes that allow
> the user to reconfigure a region that supports variants by selecting
> one variant a list of pre-defined variants. Concrete regions can enable
> variant support by implementing the new apply_variant and remove_variant
> methods and adding them to fpga_region_info before registration.
>
> As part of this RFC, the of-fpga-region concrete region has been extended
> to implement the variant interface. Variants are statically specified in
> the device tree using an fpga-variants node. Additionally, it introduces
> a firmware-cached property to cache bitstreams in memory, enabling a fast
> reconfiguration path for real-time (latency-sensitive) applications.
>
> Below is an example of how variants can be statically defined in the
> device tree under this architecture:
>
> fake_mgr: fpga-mgr@0 {
> compatible = "linux,fake-fpga-mgr";
> };
>
> fpga_region: fpga-region@0 {
> compatible = "fpga-region";
> #address-cells = <2>;
> #size-cells = <2>;
> ranges;
>
> /* FPGA region properties */
> fpga-mgr = <&fake_mgr>;
> partial-fpga-config;
> region-unfreeze-timeout-us = <10000>;
>
> /* Base variant */
> base-variant = "variant-1";
Previously we use DT-overlay file, here we use a list of variants. I'm
actually not sure why it is safer or better managed. As far as I can
tell, the only difference is that DT-overlay puts the region descriptions
in different files/blobs, while your fpga-variant puts them together in
one file/node, and we switch from choosing a file/blob to choosing a
string, is it?
>
> /* Variants container node */
> fpga-variants {
> #address-cells = <2>;
> #size-cells = <2>;
> ranges;
>
> variant-1 {
> firmware-name = "variant1-image.bin";
>
> #address-cells = <2>;
> #size-cells = <2>;
> ranges;
>
> variant_1_ip: ip_1@10000 {
> compatible = "fake,ip_1";
> reg = <0x0 0x10000 0x0 0x1000>;
> };
> };
>
> variant-2 {
> firmware-name = "variant2-image.bin";
> firmware-cached;
>
> #address-cells = <2>;
> #size-cells = <2>;
> ranges;
>
> variant_2_ip: ip_2@10000 {
> compatible = "fake,ip_2";
> reg = <0x0 0x20000 0x0 0x1000>;
> };
> };
> };
> };
^ permalink raw reply
* [PATCH v3] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
From: tze.yee.ng @ 2026-06-24 10:11 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix, Greg Kroah-Hartman,
Richard Gong, Alan Tull, linux-fpga, linux-kernel
Cc: tien.sung.ang
From: Tien Sung Ang <tien.sung.ang@altera.com>
Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.
Do not call stratix10_svc_done() at the end of write_init() on success, so
the SVC session remains active through write() and write_complete(). Call
stratix10_svc_done() on failure in write_init() and write() so the shared
SVC mailbox is released when reconfiguration aborts, allowing coexistence
with other SVC clients such as soc64-hwmon.
Fixes: e7eef1d7633a ("fpga: add intel stratix10 soc fpga manager driver")
Cc: stable@vger.kernel.org # 5.1+
Signed-off-by: Tien Sung Ang <tien.sung.ang@altera.com>
Signed-off-by: Tze Yee Ng <tze.yee.ng@altera.com>
---
Changes in v3:
- Remove blank line before Signed-off-by.
- No code changes from v2.
Changes in v2:
- Add Fixes: e7eef1d7633a per maintainer review; the incorrect
stratix10_svc_done() call in write_init() dates to the initial driver.
- Add Cc: stable@vger.kernel.org # 5.1+ for stable backport consideration.
- No code changes from v1.
---
drivers/fpga/stratix10-soc.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 0a295ccf1644..0d07d303bad1 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -195,20 +195,20 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
&ctype, sizeof(ctype));
if (ret < 0)
- goto init_done;
+ goto init_error;
ret = wait_for_completion_timeout(
&priv->status_return_completion, S10_RECONFIG_TIMEOUT);
if (!ret) {
dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
ret = 0;
if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
/* Allocate buffers from the service layer's pool. */
@@ -216,16 +216,19 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
if (IS_ERR(kbuf)) {
s10_free_buffers(mgr);
- ret = PTR_ERR(kbuf);
- goto init_done;
+ ret = -ENOMEM;
+ goto init_error;
}
priv->svc_bufs[i].buf = kbuf;
priv->svc_bufs[i].lock = 0;
}
-init_done:
+ goto init_done;
+
+init_error:
stratix10_svc_done(priv->chan);
+init_done:
return ret;
}
@@ -342,6 +345,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
if (!s10_free_buffers(mgr))
dev_err(dev, "%s not all buffers were freed\n", __func__);
+ if (ret < 0)
+ stratix10_svc_done(priv->chan);
+
return ret;
}
--
2.43.7
^ permalink raw reply related
* Re: [PATCH v2] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
From: Dinh Nguyen @ 2026-06-24 5:21 UTC (permalink / raw)
To: tze.yee.ng, Moritz Fischer, Xu Yilun, Tom Rix, Greg Kroah-Hartman,
Richard Gong, Alan Tull, linux-fpga, linux-kernel
Cc: tien.sung.ang
In-Reply-To: <b019a8ba0b0c30b0bc19719386a46d47df3141b3.1782271444.git.tze.yee.ng@altera.com>
On 6/23/26 22:37, tze.yee.ng@altera.com wrote:
> From: Tien Sung Ang <tien.sung.ang@altera.com>
>
> Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.
>
> Do not call stratix10_svc_done() at the end of write_init() on success, so
> the SVC session remains active through write() and write_complete(). Call
> stratix10_svc_done() on failure in write_init() and write() so the shared
> SVC mailbox is released when reconfiguration aborts, allowing coexistence
> with other SVC clients such as soc64-hwmon.
>
> Fixes: e7eef1d7633a ("fpga: add intel stratix10 soc fpga manager driver")
> Cc: stable@vger.kernel.org # 5.1+
>
Remove the blank line.
> Signed-off-by: Tien Sung Ang <tien.sung.ang@altera.com>
> Signed-off-by: Tze Yee Ng <tze.yee.ng@altera.com>
^ permalink raw reply
* [PATCH v2] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
From: tze.yee.ng @ 2026-06-24 3:37 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix, Greg Kroah-Hartman,
Richard Gong, Alan Tull, linux-fpga, linux-kernel
Cc: tien.sung.ang
From: Tien Sung Ang <tien.sung.ang@altera.com>
Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.
Do not call stratix10_svc_done() at the end of write_init() on success, so
the SVC session remains active through write() and write_complete(). Call
stratix10_svc_done() on failure in write_init() and write() so the shared
SVC mailbox is released when reconfiguration aborts, allowing coexistence
with other SVC clients such as soc64-hwmon.
Fixes: e7eef1d7633a ("fpga: add intel stratix10 soc fpga manager driver")
Cc: stable@vger.kernel.org # 5.1+
Signed-off-by: Tien Sung Ang <tien.sung.ang@altera.com>
Signed-off-by: Tze Yee Ng <tze.yee.ng@altera.com>
---
Changes in v2:
- Add Fixes: e7eef1d7633a per maintainer review; the incorrect
stratix10_svc_done() call in write_init() dates to the initial driver.
- Add Cc: stable@vger.kernel.org # 5.1+ for stable backport consideration.
- No code changes from v1.
---
drivers/fpga/stratix10-soc.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 0a295ccf1644..0d07d303bad1 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -195,20 +195,20 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
&ctype, sizeof(ctype));
if (ret < 0)
- goto init_done;
+ goto init_error;
ret = wait_for_completion_timeout(
&priv->status_return_completion, S10_RECONFIG_TIMEOUT);
if (!ret) {
dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
ret = 0;
if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
/* Allocate buffers from the service layer's pool. */
@@ -216,16 +216,19 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
if (IS_ERR(kbuf)) {
s10_free_buffers(mgr);
- ret = PTR_ERR(kbuf);
- goto init_done;
+ ret = -ENOMEM;
+ goto init_error;
}
priv->svc_bufs[i].buf = kbuf;
priv->svc_bufs[i].lock = 0;
}
-init_done:
+ goto init_done;
+
+init_error:
stratix10_svc_done(priv->chan);
+init_done:
return ret;
}
@@ -342,6 +345,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
if (!s10_free_buffers(mgr))
dev_err(dev, "%s not all buffers were freed\n", __func__);
+ if (ret < 0)
+ stratix10_svc_done(priv->chan);
+
return ret;
}
--
2.43.7
^ permalink raw reply related
* Re: [PATCH] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
From: NG, TZE YEE @ 2026-06-24 3:20 UTC (permalink / raw)
To: Dinh Nguyen
Cc: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <da50ce16-f1a8-4d2e-8abd-ee9900644752@kernel.org>
On 24/6/2026 12:09 am, Dinh Nguyen wrote:
>
>
> On 6/23/26 04:47, tze.yee.ng@altera.com wrote:
>> From: Tien Sung Ang <tien.sung.ang@altera.com>
>>
>> Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.
>
> Do you need a Fixes tag and Cc: stable then?
>
Yes. The incorrect stratix10_svc_done() call in write_init() has been
there since the driver was first added. I'll add the following fixes tag
in v2:
Fixes: e7eef1d7633a ("fpga: add intel stratix10 soc fpga manager driver")
Cc: stable@vger.kernel.org # 5.1+
>>
>> Do not call stratix10_svc_done() at the end of write_init() on
>> success, so
>> the SVC session remains active through write() and write_complete(). Call
>> stratix10_svc_done() on failure in write_init() and write() so the shared
>> SVC mailbox is released when reconfiguration aborts, allowing coexistence
>> with other SVC clients such as soc64-hwmon.
>>
>
^ permalink raw reply
* Re: [PATCH] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
From: Dinh Nguyen @ 2026-06-23 16:09 UTC (permalink / raw)
To: tze.yee.ng, Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga,
linux-kernel
In-Reply-To: <3fb9ad8196d00860f7d4aa2e0394a271f5ad0cec.1782208001.git.tze.yee.ng@altera.com>
On 6/23/26 04:47, tze.yee.ng@altera.com wrote:
> From: Tien Sung Ang <tien.sung.ang@altera.com>
>
> Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.
Do you need a Fixes tag and Cc: stable then?
>
> Do not call stratix10_svc_done() at the end of write_init() on success, so
> the SVC session remains active through write() and write_complete(). Call
> stratix10_svc_done() on failure in write_init() and write() so the shared
> SVC mailbox is released when reconfiguration aborts, allowing coexistence
> with other SVC clients such as soc64-hwmon.
>
^ permalink raw reply
* Re: [PATCH] fpga: xilinx-pr-decoupler: Use devm_clk_get_prepared()
From: Pandey, Radhey Shyam @ 2026-06-23 11:01 UTC (permalink / raw)
To: Michal Simek, linux-kernel, monstr, git
Cc: Moritz Fischer, Tom Rix, Xu Yilun,
moderated list:ARM/ZYNQ ARCHITECTURE,
open list:FPGA MANAGER FRAMEWORK
In-Reply-To: <8ca8ee5ba720b608a41f842d2b743302e5500ad0.1782205286.git.michal.simek@amd.com>
> The driver keeps the "aclk" clock prepared but disabled in its idle
> state, toggling only the atomic clk_enable()/clk_disable() around
> register accesses in the bridge enable_set/enable_show callbacks.
>
> At probe time this was open-coded as clk_prepare_enable() immediately
> followed by clk_disable(), leaving the clock prepared, with a matching
> clk_unprepare() in the error path and in remove().
>
> devm_clk_get_prepared() expresses exactly this: it gets and prepares the
> clock and unprepares it automatically on driver detach.
>
> Use it to drop the manual prepare/disable dance, the error-path
> unprepare, and the now-empty clock teardown in remove().
>
> Signed-off-by: Michal Simek <michal.simek@amd.com>
Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Thanks!
> ---
>
> drivers/fpga/xilinx-pr-decoupler.c | 20 ++------------------
> 1 file changed, 2 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
> index 6994d68e9036..45b65a3264af 100644
> --- a/drivers/fpga/xilinx-pr-decoupler.c
> +++ b/drivers/fpga/xilinx-pr-decoupler.c
> @@ -118,46 +118,30 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
> if (IS_ERR(priv->io_base))
> return PTR_ERR(priv->io_base);
>
> - priv->clk = devm_clk_get(&pdev->dev, "aclk");
> + priv->clk = devm_clk_get_prepared(&pdev->dev, "aclk");
> if (IS_ERR(priv->clk))
> return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
> "input clock not found\n");
>
> - err = clk_prepare_enable(priv->clk);
> - if (err) {
> - dev_err(&pdev->dev, "unable to enable clock\n");
> - return err;
> - }
> -
> - clk_disable(priv->clk);
> -
> br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
> &xlnx_pr_decoupler_br_ops, priv);
> if (IS_ERR(br)) {
> err = PTR_ERR(br);
> dev_err(&pdev->dev, "unable to register %s",
> priv->ipconfig->name);
> - goto err_clk;
> + return err;
> }
>
> platform_set_drvdata(pdev, br);
>
> return 0;
> -
> -err_clk:
> - clk_unprepare(priv->clk);
> -
> - return err;
> }
>
> static void xlnx_pr_decoupler_remove(struct platform_device *pdev)
> {
> struct fpga_bridge *bridge = platform_get_drvdata(pdev);
> - struct xlnx_pr_decoupler_data *p = bridge->priv;
>
> fpga_bridge_unregister(bridge);
> -
> - clk_unprepare(p->clk);
> }
>
> static struct platform_driver xlnx_pr_decoupler_driver = {
> ---
> base-commit: 502d801f0ab03e4f32f9a33d203154ce84887921
> branch: xnext/pr-decoupler
>
^ permalink raw reply
* [PATCH] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
From: tze.yee.ng @ 2026-06-23 9:47 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
From: Tien Sung Ang <tien.sung.ang@altera.com>
Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.
Do not call stratix10_svc_done() at the end of write_init() on success, so
the SVC session remains active through write() and write_complete(). Call
stratix10_svc_done() on failure in write_init() and write() so the shared
SVC mailbox is released when reconfiguration aborts, allowing coexistence
with other SVC clients such as soc64-hwmon.
Signed-off-by: Tien Sung Ang <tien.sung.ang@altera.com>
Signed-off-by: Tze Yee Ng <tze.yee.ng@altera.com>
---
drivers/fpga/stratix10-soc.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 0a295ccf1644..0d07d303bad1 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -195,20 +195,20 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
&ctype, sizeof(ctype));
if (ret < 0)
- goto init_done;
+ goto init_error;
ret = wait_for_completion_timeout(
&priv->status_return_completion, S10_RECONFIG_TIMEOUT);
if (!ret) {
dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
ret = 0;
if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
/* Allocate buffers from the service layer's pool. */
@@ -216,16 +216,19 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
if (IS_ERR(kbuf)) {
s10_free_buffers(mgr);
- ret = PTR_ERR(kbuf);
- goto init_done;
+ ret = -ENOMEM;
+ goto init_error;
}
priv->svc_bufs[i].buf = kbuf;
priv->svc_bufs[i].lock = 0;
}
-init_done:
+ goto init_done;
+
+init_error:
stratix10_svc_done(priv->chan);
+init_done:
return ret;
}
@@ -342,6 +345,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
if (!s10_free_buffers(mgr))
dev_err(dev, "%s not all buffers were freed\n", __func__);
+ if (ret < 0)
+ stratix10_svc_done(priv->chan);
+
return ret;
}
--
2.43.7
^ permalink raw reply related
* [PATCH] fpga: xilinx-pr-decoupler: Use devm_clk_get_prepared()
From: Michal Simek @ 2026-06-23 9:01 UTC (permalink / raw)
To: linux-kernel, monstr, michal.simek, git
Cc: Moritz Fischer, Tom Rix, Xu Yilun,
moderated list:ARM/ZYNQ ARCHITECTURE,
open list:FPGA MANAGER FRAMEWORK
The driver keeps the "aclk" clock prepared but disabled in its idle
state, toggling only the atomic clk_enable()/clk_disable() around
register accesses in the bridge enable_set/enable_show callbacks.
At probe time this was open-coded as clk_prepare_enable() immediately
followed by clk_disable(), leaving the clock prepared, with a matching
clk_unprepare() in the error path and in remove().
devm_clk_get_prepared() expresses exactly this: it gets and prepares the
clock and unprepares it automatically on driver detach.
Use it to drop the manual prepare/disable dance, the error-path
unprepare, and the now-empty clock teardown in remove().
Signed-off-by: Michal Simek <michal.simek@amd.com>
---
drivers/fpga/xilinx-pr-decoupler.c | 20 ++------------------
1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
index 6994d68e9036..45b65a3264af 100644
--- a/drivers/fpga/xilinx-pr-decoupler.c
+++ b/drivers/fpga/xilinx-pr-decoupler.c
@@ -118,46 +118,30 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
- priv->clk = devm_clk_get(&pdev->dev, "aclk");
+ priv->clk = devm_clk_get_prepared(&pdev->dev, "aclk");
if (IS_ERR(priv->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
"input clock not found\n");
- err = clk_prepare_enable(priv->clk);
- if (err) {
- dev_err(&pdev->dev, "unable to enable clock\n");
- return err;
- }
-
- clk_disable(priv->clk);
-
br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
&xlnx_pr_decoupler_br_ops, priv);
if (IS_ERR(br)) {
err = PTR_ERR(br);
dev_err(&pdev->dev, "unable to register %s",
priv->ipconfig->name);
- goto err_clk;
+ return err;
}
platform_set_drvdata(pdev, br);
return 0;
-
-err_clk:
- clk_unprepare(priv->clk);
-
- return err;
}
static void xlnx_pr_decoupler_remove(struct platform_device *pdev)
{
struct fpga_bridge *bridge = platform_get_drvdata(pdev);
- struct xlnx_pr_decoupler_data *p = bridge->priv;
fpga_bridge_unregister(bridge);
-
- clk_unprepare(p->clk);
}
static struct platform_driver xlnx_pr_decoupler_driver = {
---
base-commit: 502d801f0ab03e4f32f9a33d203154ce84887921
branch: xnext/pr-decoupler
--
2.43.0
^ permalink raw reply related
* [PATCH] fpga: altera-cvp: Handle truncated bitstream with padding buffer
From: muhammad.nazim.amirul.nazle.asmade @ 2026-06-19 4:08 UTC (permalink / raw)
To: mdf, yilun.xu; +Cc: trix, linux-fpga, linux-kernel
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
The AIB CvP firmware requires a minimum transfer size of 4096 bytes.
When a bitstream smaller than this is sent, the firmware DMA engine
stalls waiting for the remaining data, causing a hang.
Allocate a 4096-byte send_buf at probe time and copy host data into
it before each block transfer, ensuring the full block size is always
sent regardless of the actual data length. Also improve error
detection in write_complete() by checking VSE_CVP_STATUS_CFG_ERR
instead of VSE_UNCOR_ERR_CVP_CFG_ERR to catch truncated or
mismatched bitstream errors.
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
---
drivers/fpga/altera-cvp.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 44badfd11e1b..36557c5a0c3f 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -77,6 +77,7 @@ struct altera_cvp_conf {
u8 numclks;
u32 sent_packets;
u32 vsec_offset;
+ u8 *send_buf;
const struct cvp_priv *priv;
};
@@ -449,7 +450,9 @@ static int altera_cvp_write(struct fpga_manager *mgr, const char *buf,
}
len = min(conf->priv->block_size, remaining);
- altera_cvp_send_block(conf, data, len);
+ memcpy(conf->send_buf, data, len);
+ altera_cvp_send_block(conf, (const u32 *)conf->send_buf,
+ conf->priv->block_size);
data += len / sizeof(u32);
done += len;
remaining -= len;
@@ -488,10 +491,10 @@ static int altera_cvp_write_complete(struct fpga_manager *mgr,
if (ret)
return ret;
- /* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */
- altera_read_config_dword(conf, VSE_UNCOR_ERR_STATUS, &val);
- if (val & VSE_UNCOR_ERR_CVP_CFG_ERR) {
- dev_err(&mgr->dev, "detected CVP_CONFIG_ERROR_LATCHED!\n");
+ /* STEP 16 - check CVP_CONFIG_ERROR bit */
+ ret = altera_read_config_dword(conf, VSE_CVP_STATUS, &val);
+ if (ret || (val & VSE_CVP_STATUS_CFG_ERR)) {
+ dev_err(&mgr->dev, "CVP_CONFIG_ERROR!\n");
return -EPROTO;
}
@@ -650,6 +653,11 @@ static int altera_cvp_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mgr);
+ conf->send_buf = devm_kzalloc(&pdev->dev, conf->priv->block_size, GFP_KERNEL);
+ if (!conf->send_buf) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
return 0;
err_unmap:
--
2.43.7
^ permalink raw reply related
* [PATCH] fpga: altera-cvp: Add Agilex5 CVP credit register support
From: muhammad.nazim.amirul.nazle.asmade @ 2026-06-19 3:43 UTC (permalink / raw)
To: mdf, yilun.xu; +Cc: trix, linux-fpga, linux-kernel
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
Extend the CvP driver to support Agilex5, which uses a different
register layout than previous SoC FPGA variants for CVP bitstream
transfer.
Agilex5 uses a different VSEC layout (VSEC length 0x60) and a
dedicated CVP credit register at offset 0x5C with 12-bit width,
compared to offset 0x49 with 8-bit width on other platforms.
Detect the device family at probe time by reading the VSEC specific
header and checking the VSEC length field. Introduce
device_family_type to distinguish V1, V2, and V2 Agilex5 variants.
Use altera_read_config_dword() with a 0xFFF mask for Agilex5 credits
and altera_read_config_byte() for all other devices. Fix the credit
comparison by removing the incorrect u8 cast on sent_packets.
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
---
drivers/fpga/altera-cvp.c | 48 +++++++++++++++++++++++++++++++++++----
1 file changed, 43 insertions(+), 5 deletions(-)
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 44badfd11e1b..58128de2a8c5 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -10,6 +10,7 @@
* Firmware must be in binary "rbf" format.
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fpga/fpga-mgr.h>
@@ -22,6 +23,9 @@
#define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */
/* Vendor Specific Extended Capability Registers */
+#define VSE_PCIE_SPECIFIC_HEADER 0x4 /* VSEC ID, Revision, length */
+#define VSEC_LENGTH GENMASK(31, 20)
+#define AGILEX5_VSEC_LENGTH 0x60 /* Agilex5 only */
#define VSE_CVP_STATUS 0x1c /* 32bit */
#define VSE_CVP_STATUS_CFG_RDY BIT(18) /* CVP_CONFIG_READY */
#define VSE_CVP_STATUS_CFG_ERR BIT(19) /* CVP_CONFIG_ERROR */
@@ -48,6 +52,7 @@
#define V1_VSEC_OFFSET 0x200 /* Vendor Specific Offset V1 */
/* V2 Defines */
#define VSE_CVP_TX_CREDITS 0x49 /* 8bit */
+#define VSE_CVP_AG5_TX_CREDITS 0x5C /* 12bit credits for Agilex5 */
#define V2_CREDIT_TIMEOUT_US 40000
#define V2_CHECK_CREDIT_US 10
@@ -58,6 +63,9 @@
#define DRV_NAME "altera-cvp"
#define ALTERA_CVP_MGR_NAME "Altera CvP FPGA Manager"
+#define SOCFPGA_CVP_V1_OTHERS 0x1
+#define SOCFPGA_CVP_V2_OTHERS 0x2
+#define SOCFPGA_CVP_V2_AGILEX5 0x3
/* Write block sizes */
#define ALTERA_CVP_V1_SIZE 4
@@ -78,6 +86,7 @@ struct altera_cvp_conf {
u32 sent_packets;
u32 vsec_offset;
const struct cvp_priv *priv;
+ u32 device_family_type;
};
struct cvp_priv {
@@ -228,18 +237,32 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr,
u32 timeout = V2_CREDIT_TIMEOUT_US / V2_CHECK_CREDIT_US;
struct altera_cvp_conf *conf = mgr->priv;
int ret;
- u8 val;
+ u32 val;
+ u32 credit_mask = GENMASK(7, 0);
+ u32 vse_cvp_tx_credits_offset = VSE_CVP_TX_CREDITS;
+
+ if (conf->device_family_type == SOCFPGA_CVP_V2_AGILEX5) {
+ vse_cvp_tx_credits_offset = VSE_CVP_AG5_TX_CREDITS;
+ credit_mask = GENMASK(11, 0);
+ }
do {
- ret = altera_read_config_byte(conf, VSE_CVP_TX_CREDITS, &val);
+ /* READ DWORD is required for Agilex5 but READ BYTE is required for non-Agilex5 */
+ if (conf->device_family_type == SOCFPGA_CVP_V2_AGILEX5)
+ ret = altera_read_config_dword(conf, vse_cvp_tx_credits_offset, &val);
+ else
+ ret = altera_read_config_byte(conf, vse_cvp_tx_credits_offset, (u8 *)&val);
+
if (ret) {
dev_err(&conf->pci_dev->dev,
"Error reading CVP Credit Register\n");
return ret;
}
+ val = val & credit_mask;
+
/* Return if there is space in FIFO */
- if (val - (u8)conf->sent_packets)
+ if (val - conf->sent_packets)
return 0;
ret = altera_cvp_chk_error(mgr, blocks * ALTERA_CVP_V2_SIZE);
@@ -507,6 +530,8 @@ static int altera_cvp_write_complete(struct fpga_manager *mgr,
conf->priv->user_time_us);
if (ret)
dev_err(&mgr->dev, "PLD_CLK_IN_USE|USERMODE timeout\n");
+ else
+ dev_notice(&mgr->dev, "CVP write completed successfully.\n");
return ret;
}
@@ -627,10 +652,23 @@ static int altera_cvp_probe(struct pci_dev *pdev,
conf->pci_dev = pdev;
conf->write_data = altera_cvp_write_data_iomem;
- if (conf->vsec_offset == V1_VSEC_OFFSET)
+ /* To differentiate the target SOCFPGA */
+ if (conf->vsec_offset == V1_VSEC_OFFSET) {
conf->priv = &cvp_priv_v1;
- else
+ conf->device_family_type = SOCFPGA_CVP_V1_OTHERS;
+ dev_notice(&pdev->dev, "V1 target SOCFPGA detected.\n");
+ } else {
+ /* Agilex7, Stratix10, Agilex5*/
conf->priv = &cvp_priv_v2;
+ pci_read_config_dword(pdev, offset + VSE_PCIE_SPECIFIC_HEADER, ®val);
+ if (FIELD_GET(VSEC_LENGTH, regval) == AGILEX5_VSEC_LENGTH) {
+ conf->device_family_type = SOCFPGA_CVP_V2_AGILEX5;
+ dev_notice(&pdev->dev, "V2 target SOCFPGA Agilex5 detected.\n");
+ } else {
+ conf->device_family_type = SOCFPGA_CVP_V2_OTHERS;
+ dev_notice(&pdev->dev, "V2 target SOCFPGA detected.\n");
+ }
+ }
conf->map = pci_iomap(pdev, CVP_BAR, 0);
if (!conf->map) {
--
2.43.7
^ permalink raw reply related
* [PATCH] fpga: altera-cvp: Retry teardown and reset CVP state on failure
From: muhammad.nazim.amirul.nazle.asmade @ 2026-06-18 11:24 UTC (permalink / raw)
To: mdf, yilun.xu; +Cc: trix, linux-fpga, linux-kernel
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
If an incorrect bitstream is sent, the teardown may fail due to a
CFG_RDY timeout. When this happens, reset CVP_MODE and HIP_CLK_SEL
bits to clean up the hardware state and return -EAGAIN, allowing the
caller to retry. Introduce altera_cvp_recovery() to wrap this retry
logic with a maximum of CVP_TEARDOWN_MAX_RETRY attempts.
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
---
drivers/fpga/altera-cvp.c | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 44badfd11e1b..29faf6f5bde1 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -63,6 +63,8 @@
#define ALTERA_CVP_V1_SIZE 4
#define ALTERA_CVP_V2_SIZE 4096
+/* Tear-down retry */
+#define CVP_TEARDOWN_MAX_RETRY 10
/* Optional CvP config error status check for debugging */
static bool altera_cvp_chkcfg;
@@ -305,12 +307,40 @@ static int altera_cvp_teardown(struct fpga_manager *mgr,
/* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */
ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0,
conf->priv->poll_time_us);
- if (ret)
+ if (ret) {
dev_err(&mgr->dev, "CFG_RDY == 0 timeout\n");
+ goto error_path;
+ }
return ret;
+
+error_path:
+ /* reset CVP_MODE and HIP_CLK_SEL bit */
+ altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ val &= ~VSE_CVP_MODE_CTRL_HIP_CLK_SEL;
+ val &= ~VSE_CVP_MODE_CTRL_CVP_MODE;
+ altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+
+ return -EAGAIN;
+
}
+static int altera_cvp_recovery(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ int ret = 0, retry = 0;
+
+ for (retry = 0; retry < CVP_TEARDOWN_MAX_RETRY; retry++) {
+ ret = altera_cvp_teardown(mgr, info);
+ if (!ret)
+ break;
+ dev_warn(&mgr->dev,
+ "%s: [%d] Tear-down failed. Retrying\n",
+ __func__,
+ retry);
+ }
+ return ret;
+}
static int altera_cvp_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
@@ -343,7 +373,7 @@ static int altera_cvp_write_init(struct fpga_manager *mgr,
if (val & VSE_CVP_STATUS_CFG_RDY) {
dev_warn(&mgr->dev, "CvP already started, tear down first\n");
- ret = altera_cvp_teardown(mgr, info);
+ ret = altera_cvp_recovery(mgr, info);
if (ret)
return ret;
}
@@ -484,7 +514,7 @@ static int altera_cvp_write_complete(struct fpga_manager *mgr,
u32 mask, val;
int ret;
- ret = altera_cvp_teardown(mgr, info);
+ ret = altera_cvp_recovery(mgr, info);
if (ret)
return ret;
--
2.43.7
^ permalink raw reply related
* Re: [GIT PULL] FPGA Manager changes for 7.2-rc1
From: Greg KH @ 2026-06-08 19:27 UTC (permalink / raw)
To: Xu Yilun; +Cc: yilun.xu, linux-fpga, mdf
In-Reply-To: <aiD30tC74Rn2KjGM@yilunxu-OptiPlex-7050>
On Thu, Jun 04, 2026 at 11:58:10AM +0800, Xu Yilun wrote:
> The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
>
> Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
>
> are available in the Git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/fpga/linux-fpga tags/fpga-for-7.2-rc1
Pulled and pushed out, thanks.
greg k-h
^ permalink raw reply
* [RFC PATCH fpga/for-next 2/2] fpga: of-fpga-region: Add support for region variants
From: Marco Pagani @ 2026-06-08 16:42 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix; +Cc: Marco Pagani, linux-fpga, linux-kernel
In-Reply-To: <20260608164247.1998417-1-marco.pagani@linux.dev>
Extend of-fpga-region with support for FPGA region variants statically
defined in the device tree.
During probing, the code now looks for an fpga-variants container
node. If present, it parses each variant child node and registers it.
The variant that is programmed in the region at boot time is defined
by the base-variant property. Additionally, the firmware-cached
property allows caching bitstreams in memory, enabling a fast
reconfiguration path for real-time (latency-sensitive) applications.
Signed-off-by: Marco Pagani <marco.pagani@linux.dev>
---
drivers/fpga/of-fpga-region.c | 272 +++++++++++++++++++++++++++++++++-
1 file changed, 270 insertions(+), 2 deletions(-)
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index 9107a5b461d3..ea32775b55ce 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -8,6 +8,7 @@
#include <linux/fpga/fpga-bridge.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/fpga/fpga-region.h>
+#include <linux/firmware.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -18,6 +19,18 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+/**
+ * struct of_fpga_variant_priv - private data for FPGA Region variants
+ * @fw_name: name of the firmware file containing the FPGA image
+ * @fw_buf: buffer containing the cached firmware image
+ * @fw_size: size in bytes of the buffer containing the cached firmware image
+ */
+struct of_fpga_variant_priv {
+ char *fw_name;
+ void *fw_buf;
+ size_t fw_size;
+};
+
static const struct of_device_id fpga_region_of_match[] = {
{ .compatible = "fpga-region", },
{},
@@ -140,6 +153,168 @@ static int of_fpga_region_get_bridges(struct fpga_region *region)
return 0;
}
+static int of_fpga_remove_variant_child(struct device *dev, void *data)
+{
+ struct device_node *variant_np = data;
+
+ if (dev->of_node && dev->of_node->parent == variant_np) {
+ if (dev_is_platform(dev)) {
+ of_node_clear_flag(dev->of_node, OF_POPULATED);
+ platform_device_unregister(to_platform_device(dev));
+ }
+ }
+ return 0;
+}
+
+static int of_fpga_region_populate_variant(struct device *dev,
+ struct device_node *variant_np)
+{
+ struct device_node *child_np;
+
+ for_each_available_child_of_node(variant_np, child_np) {
+ if (!of_platform_device_create(child_np, NULL, dev))
+ dev_warn(dev, "failed to create device node.\n");
+ }
+
+ return 0;
+}
+
+/**
+ * of_fpga_region_apply_variant - apply a specific FPGA variant to the region
+ * @region: FPGA region to be programmed
+ * @variant: FPGA variant requested to be applied
+ *
+ * Retrieves the specific variant node from the fpga-variants container,
+ * handles firmware loading (including lazy caching if firmware-cached is
+ * set), programs the FPGA region with the variant, and finally populates
+ * the platform devices for the child IPs.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int of_fpga_region_apply_variant(struct fpga_region *region,
+ struct fpga_variant *variant)
+{
+ struct of_fpga_variant_priv *priv = variant->priv;
+ struct device *dev = ®ion->dev;
+ struct device_node *variants_np, *variant_np;
+ const struct firmware *fw;
+ struct fpga_image_info *info;
+ int ret;
+
+ variants_np = of_get_child_by_name(dev->of_node, "fpga-variants");
+ if (!variants_np)
+ return -ENODEV;
+
+ variant_np = of_get_child_by_name(variants_np, variant->name);
+ of_node_put(variants_np);
+ if (!variant_np)
+ return -ENODEV;
+
+ info = fpga_image_info_alloc(dev);
+ if (!info) {
+ ret = -ENOMEM;
+ goto err_put_node;
+ }
+
+ if (!of_property_read_bool(variant_np, "firmware-cached")) {
+ info->firmware_name = devm_kstrdup(dev, priv->fw_name, GFP_KERNEL);
+ if (!info->firmware_name) {
+ ret = -ENOMEM;
+ goto err_free_info;
+ }
+ } else if (priv->fw_buf) {
+ info->buf = priv->fw_buf;
+ info->count = priv->fw_size;
+ } else {
+ ret = request_firmware(&fw, priv->fw_name, dev);
+ if (ret) {
+ dev_err(dev, "failed to request firmware '%s'\n", priv->fw_name);
+ goto err_free_info;
+ }
+
+ priv->fw_buf = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL);
+ priv->fw_size = fw->size;
+ release_firmware(fw);
+
+ if (!priv->fw_buf) {
+ ret = -ENOMEM;
+ goto err_free_info;
+ }
+
+ info->buf = priv->fw_buf;
+ info->count = priv->fw_size;
+ }
+
+ /* Checked during probing */
+ info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
+
+ of_property_read_u32(dev->of_node, "region-unfreeze-timeout-us",
+ &info->enable_timeout_us);
+ of_property_read_u32(dev->of_node, "region-freeze-timeout-us",
+ &info->disable_timeout_us);
+ of_property_read_u32(dev->of_node, "config-complete-timeout-us",
+ &info->config_complete_timeout_us);
+
+ region->info = info;
+
+ if (info->firmware_name || info->buf) {
+ ret = fpga_region_program_fpga(region);
+ if (ret) {
+ dev_err(dev, "failed to program FPGA\n");
+ goto err_clear_info;
+ }
+ }
+
+ ret = of_fpga_region_populate_variant(dev, variant_np);
+ if (ret) {
+ dev_err(dev, "failed to populate variant IP nodes\n");
+ goto err_clear_info;
+ }
+
+ of_node_put(variant_np);
+ return 0;
+
+err_clear_info:
+ region->info = NULL;
+err_free_info:
+ fpga_image_info_free(info);
+err_put_node:
+ of_node_put(variant_np);
+ return ret;
+}
+
+/**
+ * of_fpga_region_remove_variant - remove the current FPGA variant from the region
+ * @region: FPGA region
+ * @variant: FPGA variant being removed
+ *
+ * Unregisters all devices that were populared for the variant's IPs
+ * during apply_variant() and frees the FPGA image.
+ */
+static void of_fpga_region_remove_variant(struct fpga_region *region,
+ struct fpga_variant *variant)
+{
+ struct device_node *variants_np, *variant_np;
+ struct device *dev = ®ion->dev;
+
+ variants_np = of_get_child_by_name(dev->of_node, "fpga-variants");
+ if (variants_np) {
+ variant_np = of_get_child_by_name(variants_np, variant->name);
+ of_node_put(variants_np);
+
+ if (variant_np) {
+ device_for_each_child(dev, variant_np,
+ of_fpga_remove_variant_child);
+ of_node_put(variant_np);
+ }
+ }
+
+ if (region->info) {
+ fpga_image_info_free(region->info);
+ region->info = NULL;
+ }
+}
+
/**
* child_regions_with_firmware - Used to check the child region info.
* @overlay: device node of the overlay
@@ -397,8 +572,16 @@ static int of_fpga_region_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
+ struct device_node *var_np, *base_np = NULL, *variants_np = NULL;
struct fpga_region *region;
struct fpga_manager *mgr;
+ struct fpga_region_info info = { 0 };
+ struct of_fpga_variant_priv *priv;
+ const char *base_variant_name = NULL;
+ const char *fw_name = NULL;
+ bool has_base_variant = false;
+ bool base_variant_found = false;
+ bool is_base = false;
int ret;
/* Find the FPGA mgr specified by region or parent region. */
@@ -406,20 +589,105 @@ static int of_fpga_region_probe(struct platform_device *pdev)
if (IS_ERR(mgr))
return -EPROBE_DEFER;
- region = fpga_region_register(dev, mgr, of_fpga_region_get_bridges);
+ /* Register variants support only if the device tree is well-formed */
+ if (!of_property_read_string(np, "base-variant", &base_variant_name))
+ has_base_variant = true;
+
+ if (has_base_variant) {
+ if (!of_property_read_bool(np, "partial-fpga-config")) {
+ dev_err(dev, "Region '%s' is missing 'partial-fpga-config' property.\n",
+ np->name);
+ ret = -EINVAL;
+ goto eprobe_mgr_put;
+ }
+
+ variants_np = of_get_child_by_name(np, "fpga-variants");
+ if (!variants_np) {
+ dev_err(dev, "Missing 'fpga-variants' container node.\n");
+ ret = -EINVAL;
+ goto eprobe_mgr_put;
+ }
+
+ for_each_available_child_of_node(variants_np, var_np) {
+ if (!of_property_present(var_np, "firmware-name")) {
+ dev_err(dev, "Variant '%s' is missing 'firmware-name'.\n",
+ var_np->name);
+ of_node_put(var_np);
+ ret = -EINVAL;
+ goto eprobe_mgr_put;
+ }
+
+ if (!strcmp(var_np->name, base_variant_name))
+ base_variant_found = true;
+ }
+
+ if (!base_variant_found) {
+ dev_err(dev, "base-variant '%s' does not match.\n",
+ base_variant_name);
+ ret = -EINVAL;
+ goto eprobe_mgr_put;
+ }
+
+ info.apply_variant = of_fpga_region_apply_variant;
+ info.remove_variant = of_fpga_region_remove_variant;
+ }
+
+ info.mgr = mgr;
+ info.get_bridges = of_fpga_region_get_bridges;
+
+ region = fpga_region_register_full(dev, &info);
if (IS_ERR(region)) {
ret = PTR_ERR(region);
goto eprobe_mgr_put;
}
+ if (has_base_variant && variants_np) {
+ for_each_available_child_of_node(variants_np, var_np) {
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ of_node_put(var_np);
+ ret = -ENOMEM;
+ goto eprobe_region_unregister;
+ }
+
+ of_property_read_string(var_np, "firmware-name", &fw_name);
+ priv->fw_name = devm_kstrdup(dev, fw_name, GFP_KERNEL);
+ if (!priv->fw_name) {
+ of_node_put(var_np);
+ ret = -ENOMEM;
+ goto eprobe_region_unregister;
+ }
+
+ is_base = !strcmp(var_np->name, base_variant_name);
+ if (is_base)
+ base_np = of_node_get(var_np);
+
+ ret = fpga_region_add_variant(region, var_np->name, is_base, priv);
+ if (ret) {
+ dev_err(dev, "Cannot add variant '%s'.\n", var_np->name);
+ of_node_put(var_np);
+ goto eprobe_region_unregister;
+ }
+ }
+ of_node_put(variants_np);
+ }
of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev);
- platform_set_drvdata(pdev, region);
+ if (base_np) {
+ of_fpga_region_populate_variant(®ion->dev, base_np);
+ of_node_put(base_np);
+ }
+
+ platform_set_drvdata(pdev, region);
dev_info(dev, "FPGA Region probed\n");
return 0;
+eprobe_region_unregister:
+ fpga_region_unregister(region);
eprobe_mgr_put:
+ of_node_put(base_np);
+ of_node_put(variants_np);
fpga_mgr_put(mgr);
return ret;
}
--
2.54.0
^ permalink raw reply related
* [RFC PATCH fpga/for-next 1/2] fpga: region: Add support for FPGA region variants
From: Marco Pagani @ 2026-06-08 16:42 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix; +Cc: Marco Pagani, linux-fpga, linux-kernel
In-Reply-To: <20260608164247.1998417-1-marco.pagani@linux.dev>
Extend the fpga-region to support FPGA variants. Add the logic and
the interface functions to manage a list of variants, and define the
apply_variant() and remove_variant() methods that concrete regions must
implement to define how variants are populated and depopulated.
Signed-off-by: Marco Pagani <marco.pagani@linux.dev>
---
drivers/fpga/fpga-region.c | 171 +++++++++++++++++++++++++++++++
include/linux/fpga/fpga-region.h | 32 ++++++
2 files changed, 203 insertions(+)
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index 662e8e4203ca..60404cce6188 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -80,6 +80,101 @@ static void fpga_region_put(struct fpga_region *region)
mutex_unlock(®ion->mutex);
}
+/**
+ * fpga_region_add_variant - add a new variant to the FPGA region
+ * @region: FPGA region
+ * @name: string identifier for the variant
+ * @is_enabled: whether this variant is the currently enabled variant
+ * @priv: private data to be associated with the variant
+ *
+ * Allocates a new variant and adds it to the region's variant list.
+ * If @is_enabled is true, this variant is marked as the enabled variant.
+ *
+ * Return: 0 on success, or -ENOMEM on memory allocation failure.
+ */
+int fpga_region_add_variant(struct fpga_region *region, const char *name,
+ bool is_enabled, void *priv)
+{
+ struct fpga_variant *variant;
+ char *variant_name;
+
+ variant_name = devm_kstrdup(®ion->dev, name, GFP_KERNEL);
+ if (!variant_name)
+ return -ENOMEM;
+
+ variant = devm_kzalloc(®ion->dev, sizeof(*variant), GFP_KERNEL);
+ if (!variant)
+ return -ENOMEM;
+
+ variant->priv = priv;
+ variant->name = variant_name;
+
+ mutex_lock(®ion->variant_mutex);
+ if (is_enabled)
+ region->enabled_variant = variant;
+
+ list_add_tail(&variant->node, ®ion->variant_list);
+ mutex_unlock(®ion->variant_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_region_add_variant);
+
+/**
+ * fpga_region_program_variant - switch the variant of an FPGA region
+ * @region: FPGA region
+ * @name: string identifier of the variant to enable
+ *
+ * Get the requested variant from the region's variant list. If found,
+ * and it is not already active, removes the currently enabled variant via
+ * the remove_variant() callback and applies the requested one via the
+ * apply_variant() callback.
+ *
+ * Return: 0 on success, -EINVAL if the variant is not found, or a negative
+ * error code passed up from the apply_variant() callback.
+ */
+int fpga_region_program_variant(struct fpga_region *region, const char *name)
+{
+ struct fpga_variant *variant;
+ bool found_variant = false;
+ int ret = -EINVAL;
+
+ mutex_lock(®ion->variant_mutex);
+
+ list_for_each_entry(variant, ®ion->variant_list, node) {
+ if (sysfs_streq(variant->name, name)) {
+ found_variant = true;
+ break;
+ }
+ }
+
+ if (!found_variant)
+ goto out_unlock;
+
+ if (region->enabled_variant == variant) {
+ ret = 0;
+ goto out_unlock;
+ }
+
+ if (region->enabled_variant && region->remove_variant)
+ region->remove_variant(region, region->enabled_variant);
+
+ if (region->apply_variant) {
+ ret = region->apply_variant(region, variant);
+ if (ret) {
+ dev_err(®ion->dev, "Variant programming failed!\n");
+ region->enabled_variant = NULL;
+ } else {
+ region->enabled_variant = variant;
+ }
+ }
+
+out_unlock:
+ mutex_unlock(®ion->variant_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fpga_region_program_variant);
+
/**
* fpga_region_program_fpga - program FPGA
*
@@ -174,12 +269,76 @@ static ssize_t compat_id_show(struct device *dev,
static DEVICE_ATTR_RO(compat_id);
+static ssize_t variants_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ struct fpga_variant *variant;
+ int len = 0;
+
+ mutex_lock(®ion->variant_mutex);
+ list_for_each_entry(variant, ®ion->variant_list, node)
+ len += sysfs_emit_at(buf, len, "%s\n", variant->name);
+ mutex_unlock(®ion->variant_mutex);
+
+ return len;
+}
+static DEVICE_ATTR_RO(variants);
+
+static ssize_t enabled_variant_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ ssize_t len = 0;
+
+ mutex_lock(®ion->variant_mutex);
+ if (region->enabled_variant)
+ len = sysfs_emit(buf, "%s\n", region->enabled_variant->name);
+ mutex_unlock(®ion->variant_mutex);
+
+ return len;
+}
+
+static ssize_t enabled_variant_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ int ret;
+
+ ret = fpga_region_program_variant(region, buf);
+
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(enabled_variant);
+
+static struct attribute *fpga_region_variant_attrs[] = {
+ &dev_attr_enabled_variant.attr,
+ &dev_attr_variants.attr,
+ NULL,
+};
+
+static umode_t fpga_region_variant_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct fpga_region *region = to_fpga_region(dev);
+
+ return region->apply_variant ? attr->mode : 0;
+}
+
+static const struct attribute_group fpga_region_variant_group = {
+ .attrs = fpga_region_variant_attrs,
+ .is_visible = fpga_region_variant_is_visible,
+};
+
static struct attribute *fpga_region_attrs[] = {
&dev_attr_compat_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(fpga_region);
+static const struct attribute_group *fpga_region_variant_groups[] = {
+ &fpga_region_variant_group,
+ NULL,
+};
+
/**
* __fpga_region_register_full - create and register an FPGA Region device
* @parent: device parent
@@ -216,14 +375,19 @@ __fpga_region_register_full(struct device *parent, const struct fpga_region_info
region->priv = info->priv;
region->get_bridges = info->get_bridges;
region->ops_owner = owner;
+ region->apply_variant = info->apply_variant;
+ region->remove_variant = info->remove_variant;
mutex_init(®ion->mutex);
+ mutex_init(®ion->variant_mutex);
INIT_LIST_HEAD(®ion->bridge_list);
+ INIT_LIST_HEAD(®ion->variant_list);
region->dev.class = &fpga_region_class;
region->dev.parent = parent;
region->dev.of_node = parent->of_node;
region->dev.id = id;
+ region->dev.groups = fpga_region_variant_groups;
ret = dev_set_name(®ion->dev, "region%d", id);
if (ret)
@@ -280,6 +444,13 @@ EXPORT_SYMBOL_GPL(__fpga_region_register);
*/
void fpga_region_unregister(struct fpga_region *region)
{
+ mutex_lock(®ion->variant_mutex);
+ if (region->enabled_variant && region->remove_variant) {
+ region->remove_variant(region, region->enabled_variant);
+ region->enabled_variant = NULL;
+ }
+ mutex_unlock(®ion->variant_mutex);
+
device_unregister(®ion->dev);
}
EXPORT_SYMBOL_GPL(fpga_region_unregister);
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index 5fbc05fe70a6..3b3d1f8a1189 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -9,12 +9,27 @@
struct fpga_region;
+/**
+ * struct fpga_variant - a variant configuration for an FPGA region
+ * @node: list node for the region's variant list
+ * @name: identifier of the variant
+ * @priv: private data for the region concrete implementation
+ */
+struct fpga_variant {
+ struct list_head node;
+ const char *name;
+ void *priv;
+};
+
/**
* struct fpga_region_info - collection of parameters an FPGA Region
* @mgr: fpga region manager
* @compat_id: FPGA region id for compatibility check.
+ * @variant_list: linked list of registered variants
* @priv: fpga region private data
* @get_bridges: optional function to get bridges to a list
+ * @apply_variant: optional function to apply one of the variants
+ * @remove_variant: optional function to remove one of the variants
*
* fpga_region_info contains parameters for the register_full function.
* These are separated into an info structure because they some are optional
@@ -26,6 +41,8 @@ struct fpga_region_info {
struct fpga_compat_id *compat_id;
void *priv;
int (*get_bridges)(struct fpga_region *region);
+ int (*apply_variant)(struct fpga_region *region, struct fpga_variant *variant);
+ void (*remove_variant)(struct fpga_region *region, struct fpga_variant *variant);
};
/**
@@ -37,8 +54,13 @@ struct fpga_region_info {
* @info: FPGA image info
* @compat_id: FPGA region id for compatibility check.
* @ops_owner: module containing the get_bridges function
+ * @variant_list: linked list of registered variants
+ * @enabled_variant: pointer to the currently enabled variant
+ * @variant_mutex: protects the enabled variant and variant list
* @priv: private data
* @get_bridges: optional function to get bridges to a list
+ * @apply_variant: optional function to apply one of the variants
+ * @remove_variant: optional function to remove one of the variants
*/
struct fpga_region {
struct device dev;
@@ -48,8 +70,14 @@ struct fpga_region {
struct fpga_image_info *info;
struct fpga_compat_id *compat_id;
struct module *ops_owner;
+ struct list_head variant_list;
+ struct fpga_variant *enabled_variant;
+ struct mutex variant_mutex; /* protects variant_list and enabled_variant */
void *priv;
int (*get_bridges)(struct fpga_region *region);
+ int (*apply_variant)(struct fpga_region *region, struct fpga_variant *variant);
+ void (*remove_variant)(struct fpga_region *region, struct fpga_variant *variant);
+
};
#define to_fpga_region(d) container_of(d, struct fpga_region, dev)
@@ -60,6 +88,10 @@ fpga_region_class_find(struct device *start, const void *data,
int fpga_region_program_fpga(struct fpga_region *region);
+int fpga_region_add_variant(struct fpga_region *region, const char *name,
+ bool is_enabled, void *priv);
+int fpga_region_program_variant(struct fpga_region *region, const char *name);
+
#define fpga_region_register_full(parent, info) \
__fpga_region_register_full(parent, info, THIS_MODULE)
struct fpga_region *
--
2.54.0
^ permalink raw reply related
* [RFC PATCH] fpga: region: Add support for FPGA region variants
From: Marco Pagani @ 2026-06-08 16:42 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix; +Cc: Marco Pagani, linux-fpga, linux-kernel
This RFC proposes a proof-of-concept implementation of FPGA region
variants, a mechanism that introduces a common way to handle
dynamic partial reconfiguration from userspace. The proposed approach
is safe and aligned with the mainline kernel's stance on hardware
management by constraining the hardware to a mutually exclusive set
of configurations (variants) defined upfront. This is a realistic
assumption for FPGAs, as regions are typically statically defined and
synthesized during the system design phase. To keep the architecture
realistic, the following additional constraints are introduced:
(i) variants cannot be nested, and (ii) variants cannot contain FPGA
bridges.
The interface and core logic for the variant mechanism are defined
in the fpga-region and implemented in a backwards-compatible way.
The fpga-region now optionally exports sysfs attributes that allow
the user to reconfigure a region that supports variants by selecting
one variant a list of pre-defined variants. Concrete regions can enable
variant support by implementing the new apply_variant and remove_variant
methods and adding them to fpga_region_info before registration.
As part of this RFC, the of-fpga-region concrete region has been extended
to implement the variant interface. Variants are statically specified in
the device tree using an fpga-variants node. Additionally, it introduces
a firmware-cached property to cache bitstreams in memory, enabling a fast
reconfiguration path for real-time (latency-sensitive) applications.
Below is an example of how variants can be statically defined in the
device tree under this architecture:
fake_mgr: fpga-mgr@0 {
compatible = "linux,fake-fpga-mgr";
};
fpga_region: fpga-region@0 {
compatible = "fpga-region";
#address-cells = <2>;
#size-cells = <2>;
ranges;
/* FPGA region properties */
fpga-mgr = <&fake_mgr>;
partial-fpga-config;
region-unfreeze-timeout-us = <10000>;
/* Base variant */
base-variant = "variant-1";
/* Variants container node */
fpga-variants {
#address-cells = <2>;
#size-cells = <2>;
ranges;
variant-1 {
firmware-name = "variant1-image.bin";
#address-cells = <2>;
#size-cells = <2>;
ranges;
variant_1_ip: ip_1@10000 {
compatible = "fake,ip_1";
reg = <0x0 0x10000 0x0 0x1000>;
};
};
variant-2 {
firmware-name = "variant2-image.bin";
firmware-cached;
#address-cells = <2>;
#size-cells = <2>;
ranges;
variant_2_ip: ip_2@10000 {
compatible = "fake,ip_2";
reg = <0x0 0x20000 0x0 0x1000>;
};
};
};
};
Marco Pagani (2):
fpga: region: Add support for FPGA region variants
fpga: of-fpga-region: Add support for region variants
drivers/fpga/fpga-region.c | 171 +++++++++++++++++++
drivers/fpga/of-fpga-region.c | 272 ++++++++++++++++++++++++++++++-
include/linux/fpga/fpga-region.h | 32 ++++
3 files changed, 473 insertions(+), 2 deletions(-)
--
2.54.0
^ permalink raw reply
* [GIT PULL] FPGA Manager changes for 7.2-rc1
From: Xu Yilun @ 2026-06-04 3:58 UTC (permalink / raw)
To: gregkh; +Cc: yilun.xu, yilun.xu, linux-fpga, mdf
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/fpga/linux-fpga tags/fpga-for-7.2-rc1
for you to fetch changes up to 43a1974da6bc7ce8f4d1dc1d03d56997428c29c3:
fpga: microchip-spi: fix zero header_size OOB read in mpf_ops_parse_header() (2026-06-01 12:29:40 +0800)
----------------------------------------------------------------
FPGA Manager changes for 7.2-rc1
- Wentao fixes use-after-free issue in of-fpga-region
- Phil adds the of matching for Altera ts73xx fpga driver
- Ian adds Efinix FPGA SPI programming driver
- Andy fixes several minor issues for Lattice FPGA driver
- Manish converts dt-bindings to YAML schema for several Altera devices
- Sebastian fixes several minor issues for Intel DFL & Microchip FPGAs
All patches have been reviewed on the mailing list, and have been in the
last linux-next releases (as part of our for-next branch).
Signed-off-by: Xu Yilun <yilun.xu@intel.com>
----------------------------------------------------------------
Andy Shevchenko (4):
fpga: lattice-sysconfig-spi: Fix the terminator entries in ID tables
fpga: lattice-sysconfig-spi: Drop of_match_ptr() protection
fpga: lattice-sysconfig-spi: Don't use "proxy" headers
fpga: lattice-sysconfig-spi: simplify with spi_get_device_match_data()
Ian Dannapel (3):
dt-bindings: vendor-prefix: Add prefix for Efinix, Inc.
dt-bindings: fpga: Add Efinix SPI programming bindings
fpga-mgr: Add Efinix SPI programming driver
Manish Baing (2):
dt-bindings: fpga: altr,a10-pr-ip: convert to DT schema
dt-bindings: fpga: altr,socfpga-fpga-mgr: convert to DT schema
Phil Pemberton (2):
dt-bindings: fpga: Add Technologic Systems TS-7300 FPGA Manager
fpga: ts73xx-fpga: add OF match table for device tree probing
Sebastian Alba Vives (3):
fpga: dfl: add bounds check in dfh_get_param_size()
fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region()
fpga: microchip-spi: fix zero header_size OOB read in mpf_ops_parse_header()
Wentao Liang (1):
fpga: region: fix use-after-free in child_regions_with_firmware()
.../devicetree/bindings/fpga/altera-pr-ip.txt | 12 -
.../bindings/fpga/altera-socfpga-fpga-mgr.txt | 17 --
.../devicetree/bindings/fpga/altr,a10-pr-ip.yaml | 34 +++
.../bindings/fpga/altr,socfpga-fpga-mgr.yaml | 38 +++
.../bindings/fpga/efinix,trion-config.yaml | 98 ++++++++
.../bindings/fpga/technologic,ts7300-fpga.yaml | 36 +++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
drivers/fpga/Kconfig | 7 +
drivers/fpga/Makefile | 1 +
drivers/fpga/dfl-afu-main.c | 3 +
drivers/fpga/dfl.c | 2 +
drivers/fpga/efinix-spi.c | 260 +++++++++++++++++++++
drivers/fpga/lattice-sysconfig-spi.c | 29 ++-
drivers/fpga/microchip-spi.c | 3 +
drivers/fpga/of-fpga-region.c | 3 +-
drivers/fpga/ts73xx-fpga.c | 9 +
16 files changed, 507 insertions(+), 47 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/fpga/altera-pr-ip.txt
delete mode 100644 Documentation/devicetree/bindings/fpga/altera-socfpga-fpga-mgr.txt
create mode 100644 Documentation/devicetree/bindings/fpga/altr,a10-pr-ip.yaml
create mode 100644 Documentation/devicetree/bindings/fpga/altr,socfpga-fpga-mgr.yaml
create mode 100644 Documentation/devicetree/bindings/fpga/efinix,trion-config.yaml
create mode 100644 Documentation/devicetree/bindings/fpga/technologic,ts7300-fpga.yaml
create mode 100644 drivers/fpga/efinix-spi.c
^ permalink raw reply
* Re: [PATCH v2 2/2] MAINTAINERS: update PTP maintainer entries after directory split
From: David Woodhouse @ 2026-06-03 22:55 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Richard Cochran, Wen Gu, tglx, andrew+netdev, davem, edumazet,
pabeni, linux-kernel, netdev, jstultz, anna-maria, frederic,
daniel.lezcano, sboyd, vladimir.oltean, wei.fang, xiaoning.wang,
jonathan.lemon, vadim.fedorenko, yangbo.lu, svens, nick.shi,
ajay.kaher, alexey.makhalov, bcm-kernel-feedback-list, linux-fpga,
imx, linux-s390, dust.li, xuanzhuo, mani, imran.shaik, taniya.das
In-Reply-To: <20260602190357.62c04d40@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 1339 bytes --]
On Tue, 2026-06-02 at 19:03 -0700, Jakub Kicinski wrote:
> On Tue, 02 Jun 2026 09:04:27 +0100 David Woodhouse wrote:
> > > Even though the "PTP" naming was an unfortunate choice way back when,
> > > still I'm not a big fan of moving stuff around "just because".
> > >
> > > But moving forward, I would suggest starting a new area for pure
> > > hardware clock devices.
> >
> > I think that ties relatively well to Jakub's "does it purport to know
> > real time better than the host" criterion?
> >
> > Although... ENA *both* purports to know real time better than the host
> > *and* does packet timestamping, and it looks like GVE is attempting to
> > do the same?
>
> FWIW the ENA driver as it exists upstream does not currently support
> packet timestamping. Or at least the usual terms for which I'm grepping
> do not hit anything in this driver. Only skb_tx_timestamp() which is SW
> timestamping
>
> Given various Google projects related to use of time / latency for
> networking I think you're right that GVE is likely the first driver
> that will straddle the boundary. For those drivers which are both NIC
> and virt we can stick to net-next, just always wait for your review?
No need to block on me unless you want to. I'll just take the things
off your hands that you actively don't want.
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/2] MAINTAINERS: update PTP maintainer entries after directory split
From: Jakub Kicinski @ 2026-06-03 2:03 UTC (permalink / raw)
To: David Woodhouse
Cc: Richard Cochran, Wen Gu, tglx, andrew+netdev, davem, edumazet,
pabeni, linux-kernel, netdev, jstultz, anna-maria, frederic,
daniel.lezcano, sboyd, vladimir.oltean, wei.fang, xiaoning.wang,
jonathan.lemon, vadim.fedorenko, yangbo.lu, svens, nick.shi,
ajay.kaher, alexey.makhalov, bcm-kernel-feedback-list, linux-fpga,
imx, linux-s390, dust.li, xuanzhuo, mani, imran.shaik, taniya.das
In-Reply-To: <0b3f00bbfa6bcc3badb4d1bb7845326e2dbaa1d4.camel@infradead.org>
On Tue, 02 Jun 2026 09:04:27 +0100 David Woodhouse wrote:
> > Even though the "PTP" naming was an unfortunate choice way back when,
> > still I'm not a big fan of moving stuff around "just because".
> >
> > But moving forward, I would suggest starting a new area for pure
> > hardware clock devices.
>
> I think that ties relatively well to Jakub's "does it purport to know
> real time better than the host" criterion?
>
> Although... ENA *both* purports to know real time better than the host
> *and* does packet timestamping, and it looks like GVE is attempting to
> do the same?
FWIW the ENA driver as it exists upstream does not currently support
packet timestamping. Or at least the usual terms for which I'm grepping
do not hit anything in this driver. Only skb_tx_timestamp() which is SW
timestamping
Given various Google projects related to use of time / latency for
networking I think you're right that GVE is likely the first driver
that will straddle the boundary. For those drivers which are both NIC
and virt we can stick to net-next, just always wait for your review?
^ permalink raw reply
* Re: [PATCH v2 2/2] MAINTAINERS: update PTP maintainer entries after directory split
From: David Woodhouse @ 2026-06-02 17:15 UTC (permalink / raw)
To: Wen Gu, Richard Cochran, Jakub Kicinski
Cc: tglx, andrew+netdev, davem, edumazet, pabeni, linux-kernel,
netdev, jstultz, anna-maria, frederic, daniel.lezcano, sboyd,
vladimir.oltean, wei.fang, xiaoning.wang, jonathan.lemon,
vadim.fedorenko, yangbo.lu, svens, nick.shi, ajay.kaher,
alexey.makhalov, bcm-kernel-feedback-list, linux-fpga, imx,
linux-s390, dust.li, xuanzhuo, mani, imran.shaik, taniya.das
In-Reply-To: <335ae01a-20aa-439e-996c-d35ffd8d476a@linux.alibaba.com>
[-- Attachment #1: Type: text/plain, Size: 1638 bytes --]
On Tue, 2026-06-02 at 22:03 +0800, Wen Gu wrote:
>
> > There is some extra stuff we want to do for "Precision RTCs" or
> > whatever we're going to call them. They might actually have a known TAI
> > offset, they might convey leap second indications, we might want to set
> > the kernel's CLOCK_REALTIME from them at boot. And in the case of
> > VMClock, I'm working on being able to clamp the kernel's timekeeping to
> > it directly².
> >
> > So maybe what we want is linux/drivers/phc, to host those read-only
> > devices which know real time. They can provide a simplified
> > implementation; maybe *only* a function like vmclock_get_crosststamp(),
> > which is just called in various different permutations by the various
> > different PTP methods.
> >
> > The core linux/drivers/phc code would then handle the interface to the
> > kernel's core timekeeping *and* wrap them to register a PTP device that
> > existing userspace can understand. And deal with the kvmclock/TSC
> > awfulness where needed.
> >
> > How does that sound?
> >
>
> I think a dedicated phc core would make the classification of read-only
> clocks clearer, reducing ambiguity around where they belong. I assume
> direct timekeeping integration would be optional, drivers providing
> only a snapshot-based crosststamp would use /dev/ptpX alone, while the
> timekeeping path would require additional capability (as vmclock provides)?
Yeah. I think they might all want to be able to opt in to setting the
time at boot? But actually setting the fine-tuning of the kernel's
timekeeper is probably only something vmclock can do.
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/2] MAINTAINERS: update PTP maintainer entries after directory split
From: Wen Gu @ 2026-06-02 14:03 UTC (permalink / raw)
To: David Woodhouse, Richard Cochran, Jakub Kicinski
Cc: tglx, andrew+netdev, davem, edumazet, pabeni, linux-kernel,
netdev, jstultz, anna-maria, frederic, daniel.lezcano, sboyd,
vladimir.oltean, wei.fang, xiaoning.wang, jonathan.lemon,
vadim.fedorenko, yangbo.lu, svens, nick.shi, ajay.kaher,
alexey.makhalov, bcm-kernel-feedback-list, linux-fpga, imx,
linux-s390, dust.li, xuanzhuo, mani, imran.shaik, taniya.das
In-Reply-To: <0b3f00bbfa6bcc3badb4d1bb7845326e2dbaa1d4.camel@infradead.org>
On 2026/6/2 16:04, David Woodhouse wrote:
> On Mon, 2026-06-01 at 21:20 -0700, Richard Cochran wrote:
>>
>> Still, I don't understand why these new (non-network related ) device
>> drivers can't be implemented in their own class using
>> posix_clock_register()
>> etc.
>>
>> Any of the useful bits (like sysfs interfaces) can be refactored out
>> of ptp_clock.c and shared as a common layer.
>
> The key is that userspace wants to get snapshots of the reference clock
> either precisely synchronized with the system clock where possible, or
> sandwiched as closely together as possible with ABA readings of the
> system, reference, system clock.
>
> Those *are* the PTP_SYS_OFFSET* ioctls, which tools like chrony already
> support with 'refclock PHC /dev/ptp…'.
>
> I'm not sure how factoring those out into separate POSIX AUX clocks
> would help? I think they do want to present as /dev/ptp*.
>
I agree. Both [1] and [2] independently support the same point:
chrony's PHC refclock relies on ptp-specific ioctls (in sys_linux.c [3]),
not just clock_gettime(), so the ioctl interface needs to stay.
A posix_clock-based class wouldn't provide that.
[1] https://lore.kernel.org/netdev/2a4c9a00-45f5-4f6a-90c4-492ea1d50b79@linux.alibaba.com/
[2] https://lore.kernel.org/all/vmwwnl3zv26lmmuqp2vqltg2fudalpc5jrw7k6ifg6l5cwlk3j@i7jm62zcsl67/
[3] https://gitlab.com/chrony/chrony/-/blob/master/sys_linux.c
> There is some extra stuff we want to do for "Precision RTCs" or
> whatever we're going to call them. They might actually have a known TAI
> offset, they might convey leap second indications, we might want to set
> the kernel's CLOCK_REALTIME from them at boot. And in the case of
> VMClock, I'm working on being able to clamp the kernel's timekeeping to
> it directly².
>
> So maybe what we want is linux/drivers/phc, to host those read-only
> devices which know real time. They can provide a simplified
> implementation; maybe *only* a function like vmclock_get_crosststamp(),
> which is just called in various different permutations by the various
> different PTP methods.
>
> The core linux/drivers/phc code would then handle the interface to the
> kernel's core timekeeping *and* wrap them to register a PTP device that
> existing userspace can understand. And deal with the kvmclock/TSC
> awfulness where needed.
>
> How does that sound?
>
I think a dedicated phc core would make the classification of read-only
clocks clearer, reducing ambiguity around where they belong. I assume
direct timekeeping integration would be optional, drivers providing
only a snapshot-based crosststamp would use /dev/ptpX alone, while the
timekeeping path would require additional capability (as vmclock provides)?
>
> ² https://lore.kernel.org/all/20260520135207.37826-9-dwmw2@infradead.org/
>
>
>
>
>
^ permalink raw reply
page: next (older)
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox