[PATCH] fpga: altera-cvp: Extend wrapped HW credit counter in software
[email protected] Sun, 10 May 2026 22:38:58 -0700
| Newsgroups | org.kernel.vger.linux-fpga,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260511053858.30921-1-muhammad.nazim.amirul.nazle.asmade@altera.com> |
From: Nazim Amirul <[email protected]> Mask the hardware credit value using compound assignment and replace the simple masked comparison with a software-extended counter that handles wrap-around. The HW credit register is limited to n bits and wraps naturally. The previous logic compared the masked value directly against sent_packets, which breaks once the counter wraps. Extend the counter in software by tracking forward wraps and accumulating an overflow counter. A wrap is detected when the masked value decreases by more than half of the modulo, avoiding false detection from small backward glitches. Compute a monotonic real_credit from the overflow counter and the masked value, then compare it directly with sent_packets. This ensures correct FIFO space detection across wraps. Also add error reporting when the credit appears to move behind the sent count. Signed-off-by: Nazim Amirul <[email protected]> --- drivers/fpga/altera-cvp.c | 58 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 44badfd11e1b..6e7a8ced2d68 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -76,6 +76,9 @@ struct altera_cvp_conf { char mgr_name[64]; u8 numclks; u32 sent_packets; + u32 overflow_counter; + u32 last_credit_hw; + bool credit_ext_inited; u32 vsec_offset; const struct cvp_priv *priv; }; @@ -228,19 +231,64 @@ 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; + u32 vse_cvp_tx_credits_offset = VSE_CVP_TX_CREDITS; + u32 mod; + u32 real_credit; 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) { + vse_cvp_tx_credits_offset = VSE_CVP_AG5_TX_CREDITS; + credit_mask = 0xFFF; + ret = altera_read_config_dword(conf, vse_cvp_tx_credits_offset, &val); + } else { + /* + * For the byte config read path, val is zeroed before pci_read_config_byte + * so only the low byte is defined before masking. + */ + val = 0; + credit_mask = 0xFF; + 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; } - /* Return if there is space in FIFO */ - if (val - (u8)conf->sent_packets) + val &= credit_mask; + mod = credit_mask + 1; + + /* + * HW credit is n bits and wraps; extend it in software so we can + * compare to sent_packets directly. On a forward wrap the masked + * value jumps from high to low; require (last - val) > mod/2 so a + * small backward glitch is not counted as a wrap. More than one + * wrap between polls is not detected if the low bits repeat. + */ + if (conf->credit_ext_inited && + val < conf->last_credit_hw && + (conf->last_credit_hw - val) > (mod / 2)) + conf->overflow_counter++; + + conf->last_credit_hw = val; + conf->credit_ext_inited = true; + + real_credit = conf->overflow_counter * mod + val; + + if (real_credit > conf->sent_packets) return 0; + if (real_credit < conf->sent_packets) { + dev_err(&conf->pci_dev->dev, + "CVP credit behind sent count (real %u reg 0x%x sent %u)\n", + real_credit, val, conf->sent_packets); + return -EPROTO; + } ret = altera_cvp_chk_error(mgr, blocks * ALTERA_CVP_V2_SIZE); if (ret) { @@ -378,6 +426,8 @@ static int altera_cvp_write_init(struct fpga_manager *mgr, } conf->sent_packets = 0; + conf->overflow_counter = 0; + conf->credit_ext_inited = false; /* STEP 4 - set CVP_CONFIG bit */ altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val); -- 2.43.7