[PATCH v2] fpga: altera-cvp: Add Agilex5 CVP credit register support
From: tze . yee . ng
Date: Wed Oct 28 2026 - 06:45:14 EST
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@xxxxxxxxxx>
Agilex5 uses a different VSEC layout from earlier SoC FPGA variants:
the VSEC length is 0x60 and the CVP TX credit register moves to
offset 0x5C, a dword register holding 12-bit credits, instead of the
8-bit register at offset 0x49.
Describe the credit register differences in struct cvp_priv by adding
a read_credit() callback and a credit_mask, and add a cvp_priv for
Agilex5 using them. At probe time, read the standard PCIe VSEC header
and select the Agilex5 cvp_priv when the VSEC length is 0x60.
The credit comparison masks sent_packets with the per-variant
credit_mask so it matches the width of the hardware counter. This is
the same comparison as before for existing devices, whose counter is
8 bits wide.
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@xxxxxxxxxx>
Signed-off-by: Tze Yee Ng <tze.yee.ng@xxxxxxxxxx>
---
Note: I am taking over this patch from Nazim Amirul, who is no longer
working on it, to address the review comments. Nazim is kept as the
author.
Changes in v2:
- Use PCI_VNDR_HEADER/PCI_VNDR_HEADER_LEN() from pci_regs.h instead of
private VSEC header macros
- Describe the Agilex5 credit register via struct cvp_priv
(read_credit(), credit_mask, cvp_priv_agilex5); drop
device_family_type and the SOCFPGA_CVP_* macros
- Keep the sent_packets comparison width-limited via credit_mask; no
longer described as a fix
- Drop the dev_notice() messages
- Rewrite the commit message
v1: https://lore.kernel.org/all/20260619034327.26966-1-muhammad.nazim.amirul.nazle.asmade@xxxxxxxxxx/
---
drivers/fpga/altera-cvp.c | 57 ++++++++++++++++++++++++++++++++++-----
1 file changed, 51 insertions(+), 6 deletions(-)
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 507e29d3a79f..d5f53965c744 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -23,6 +23,7 @@
#define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */
/* Vendor Specific Extended Capability Registers */
+#define AGILEX5_VSEC_LENGTH 0x60 /* VSEC length, 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 */
@@ -49,6 +50,9 @@
#define V1_VSEC_OFFSET 0x200 /* Vendor Specific Offset V1 */
/* V2 Defines */
#define VSE_CVP_TX_CREDITS 0x49 /* 8bit */
+#define VSE_CVP_TX_CREDITS_MASK GENMASK(7, 0)
+#define VSE_CVP_AG5_TX_CREDITS 0x5c /* 12bit credits for Agilex5 */
+#define VSE_CVP_AG5_TX_CREDITS_MASK GENMASK(11, 0)
#define V2_CREDIT_TIMEOUT_US 40000
#define V2_CHECK_CREDIT_US 10
@@ -85,6 +89,8 @@ struct cvp_priv {
void (*switch_clk)(struct altera_cvp_conf *conf);
int (*clear_state)(struct altera_cvp_conf *conf);
int (*wait_credit)(struct fpga_manager *mgr, u32 blocks);
+ int (*read_credit)(struct altera_cvp_conf *conf, u32 *credits);
+ u32 credit_mask;
size_t block_size;
int poll_time_us;
int user_time_us;
@@ -223,24 +229,46 @@ static int altera_cvp_v2_clear_state(struct altera_cvp_conf *conf)
conf->priv->poll_time_us);
}
+static int altera_cvp_v2_read_credit(struct altera_cvp_conf *conf,
+ u32 *credits)
+{
+ u8 val;
+ int ret;
+
+ ret = altera_read_config_byte(conf, VSE_CVP_TX_CREDITS, &val);
+ if (!ret)
+ *credits = val;
+
+ return ret;
+}
+
+static int altera_cvp_ag5_read_credit(struct altera_cvp_conf *conf,
+ u32 *credits)
+{
+ return altera_read_config_dword(conf, VSE_CVP_AG5_TX_CREDITS, credits);
+}
+
static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr,
u32 blocks)
{
u32 timeout = V2_CREDIT_TIMEOUT_US / V2_CHECK_CREDIT_US;
struct altera_cvp_conf *conf = mgr->priv;
+ u32 mask = conf->priv->credit_mask;
int ret;
- u8 val;
+ u32 val;
do {
- ret = altera_read_config_byte(conf, VSE_CVP_TX_CREDITS, &val);
+ ret = conf->priv->read_credit(conf, &val);
if (ret) {
dev_err(&conf->pci_dev->dev,
"Error reading CVP Credit Register\n");
return ret;
}
+ val &= mask;
+
/* Return if there is space in FIFO */
- if (val - (u8)conf->sent_packets)
+ if (val - (conf->sent_packets & mask))
return 0;
ret = altera_cvp_chk_error(mgr, blocks * ALTERA_CVP_V2_SIZE);
@@ -559,6 +587,18 @@ static const struct cvp_priv cvp_priv_v1 = {
static const struct cvp_priv cvp_priv_v2 = {
.clear_state = altera_cvp_v2_clear_state,
.wait_credit = altera_cvp_v2_wait_for_credit,
+ .read_credit = altera_cvp_v2_read_credit,
+ .credit_mask = VSE_CVP_TX_CREDITS_MASK,
+ .block_size = ALTERA_CVP_V2_SIZE,
+ .poll_time_us = V2_POLL_TIMEOUT_US,
+ .user_time_us = V2_USER_TIMEOUT_US,
+};
+
+static const struct cvp_priv cvp_priv_agilex5 = {
+ .clear_state = altera_cvp_v2_clear_state,
+ .wait_credit = altera_cvp_v2_wait_for_credit,
+ .read_credit = altera_cvp_ag5_read_credit,
+ .credit_mask = VSE_CVP_AG5_TX_CREDITS_MASK,
.block_size = ALTERA_CVP_V2_SIZE,
.poll_time_us = V2_POLL_TIMEOUT_US,
.user_time_us = V2_USER_TIMEOUT_US,
@@ -658,10 +698,15 @@ 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)
+ if (conf->vsec_offset == V1_VSEC_OFFSET) {
conf->priv = &cvp_priv_v1;
- else
- conf->priv = &cvp_priv_v2;
+ } else {
+ pci_read_config_dword(pdev, offset + PCI_VNDR_HEADER, ®val);
+ if (PCI_VNDR_HEADER_LEN(regval) == AGILEX5_VSEC_LENGTH)
+ conf->priv = &cvp_priv_agilex5;
+ else
+ conf->priv = &cvp_priv_v2;
+ }
conf->map = pci_iomap(pdev, CVP_BAR, 0);
if (!conf->map) {
--
2.43.7