[PATCH v2 5/5] drivers: fpga: Add partial reconfig support for Altera SoCs
Naresh Kumar Ravulapalli
nareshkumar.ravulapalli at altera.com
Sat May 24 22:37:37 CEST 2025
Support for Altera SoCs which support partial reconfiguration is
added. This functionality will enable these devices to perform
partial reconfiguration on the specified regions.
Signed-off-by: Naresh Kumar Ravulapalli <nareshkumar.ravulapalli at altera.com>
---
drivers/fpga/altera.c | 163 ++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/fpga.c | 11 +++
include/altera.h | 7 ++
3 files changed, 181 insertions(+)
diff --git a/drivers/fpga/altera.c b/drivers/fpga/altera.c
index 64fda3a307c..978250147f0 100644
--- a/drivers/fpga/altera.c
+++ b/drivers/fpga/altera.c
@@ -5,6 +5,8 @@
*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland at enterasys.com.
+ *
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
*/
#define LOG_CATEGORY UCLASS_FPGA
@@ -20,6 +22,29 @@
#include <ACEX1K.h>
#include <log.h>
#include <stratixII.h>
+#include <errno.h>
+#include <wait_bit.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/libfdt.h>
+#include <fdtdec.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define FREEZE_CSR_STATUS_OFFSET 0
+#define FREEZE_CSR_CTRL_OFFSET 4
+#define FREEZE_CSR_ILLEGAL_REQ_OFFSET 8
+#define FREEZE_CSR_REG_VERSION 12
+
+#define FREEZE_TIMEOUT 20000
+
+#define FREEZE_CSR_STATUS_FREEZE_REQ_DONE BIT(0)
+#define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE BIT(1)
+
+#define FREEZE_CSR_CTRL_FREEZE_REQ BIT(0)
+#define FREEZE_CSR_CTRL_RESET_REQ BIT(1)
+#define FREEZE_CSR_CTRL_UNFREEZE_REQ BIT(2)
static const struct altera_fpga {
enum altera_family family;
@@ -218,3 +243,141 @@ int altera_info(Altera_desc *desc)
return FPGA_SUCCESS;
}
+
+#if IS_ENABLED(CONFIG_FPGA_PR)
+static int altera_get_freeze_br_addr(fdt_addr_t *addr, unsigned int region)
+{
+ int offset;
+ char freeze_br[12];
+ struct fdt_resource r;
+ int ret;
+
+ snprintf(freeze_br, sizeof(freeze_br), "freeze-br%d", region);
+
+ const char *alias = fdt_get_alias(gd->fdt_blob, freeze_br);
+
+ if (!alias) {
+ printf("alias %s not found in dts\n", freeze_br);
+ return -ENODEV;
+ }
+
+ offset = fdt_path_offset(gd->fdt_blob, alias);
+ if (offset < 0) {
+ printf("%s not found in dts\n", alias);
+ return -ENODEV;
+ }
+
+ ret = fdt_get_resource(gd->fdt_blob, offset, "reg", 0, &r);
+ if (ret) {
+ printf("%s has no 'reg' property!\n", freeze_br);
+ return ret;
+ }
+
+ *addr = r.start;
+
+ return ret;
+}
+
+static int altera_freeze_br_req_ack(fdt_addr_t addr, u32 req_ack)
+{
+ u32 status, illegal, ctrl;
+ int ret = -ETIMEDOUT;
+ unsigned long start = get_timer(0);
+
+ while (1) {
+ illegal = readl(addr + FREEZE_CSR_ILLEGAL_REQ_OFFSET);
+ if (illegal) {
+ printf("illegal request 0x%08x detected in freeze bridge\n", illegal);
+
+ writel(illegal, addr + FREEZE_CSR_ILLEGAL_REQ_OFFSET);
+
+ illegal = readl(addr + FREEZE_CSR_ILLEGAL_REQ_OFFSET);
+ if (illegal)
+ printf("illegal request 0x%08x detected in freeze bridge are not cleared\n",
+ illegal);
+
+ ret = -EINVAL;
+ break;
+ }
+
+ status = readl(addr + FREEZE_CSR_STATUS_OFFSET);
+ status &= req_ack;
+ if (status) {
+ ctrl = readl(addr + FREEZE_CSR_CTRL_OFFSET);
+ printf("%s request %x acknowledged %x ctrl %x\n",
+ __func__, req_ack, status, ctrl);
+
+ ret = 0;
+ break;
+ }
+
+ if (get_timer(start) > FREEZE_TIMEOUT)
+ break;
+
+ udelay(1);
+ schedule();
+ }
+
+ return ret;
+}
+
+int altera_freeze(unsigned int region)
+{
+ u32 status;
+ int ret;
+ fdt_addr_t addr;
+
+ ret = altera_get_freeze_br_addr(&addr, region);
+ if (ret)
+ return ret;
+
+ status = readl(addr + FREEZE_CSR_STATUS_OFFSET);
+
+ if (status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE)
+ return 0;
+
+ if (!(status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE))
+ return -EINVAL;
+
+ writel(FREEZE_CSR_CTRL_FREEZE_REQ, addr + FREEZE_CSR_CTRL_OFFSET);
+
+ ret = altera_freeze_br_req_ack(addr, FREEZE_CSR_STATUS_FREEZE_REQ_DONE);
+ if (ret) {
+ writel(0, addr + FREEZE_CSR_CTRL_OFFSET);
+ debug("%s: freeze request not acknowledged\n", __func__);
+ } else {
+ writel(FREEZE_CSR_CTRL_RESET_REQ, addr + FREEZE_CSR_CTRL_OFFSET);
+ }
+
+ return ret;
+}
+
+int altera_unfreeze(unsigned int region)
+{
+ u32 status;
+ int ret;
+ fdt_addr_t addr;
+
+ ret = altera_get_freeze_br_addr(&addr, region);
+ if (ret)
+ return ret;
+
+ writel(0, addr + FREEZE_CSR_CTRL_OFFSET);
+
+ status = readl(addr + FREEZE_CSR_STATUS_OFFSET);
+
+ if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)
+ return 0;
+
+ if (!(status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE))
+ return -EINVAL;
+
+ writel(FREEZE_CSR_CTRL_UNFREEZE_REQ, addr + FREEZE_CSR_CTRL_OFFSET);
+
+ ret = altera_freeze_br_req_ack(addr, FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE);
+
+ writel(0, addr + FREEZE_CSR_CTRL_OFFSET);
+
+ return ret;
+}
+#endif
diff --git a/drivers/fpga/fpga.c b/drivers/fpga/fpga.c
index c1d10c9cad4..55f42b9bd7e 100644
--- a/drivers/fpga/fpga.c
+++ b/drivers/fpga/fpga.c
@@ -411,6 +411,17 @@ int fpga_pr(int devnum, const char *cmd, unsigned int region)
if (desc) {
switch (desc->devtype) {
+ case fpga_altera:
+ if (IS_ENABLED(CONFIG_FPGA_ALTERA)) {
+ if (strcmp(cmd, "start") == 0)
+ ret_val = altera_freeze(region);
+ else if (strcmp(cmd, "stop") == 0)
+ ret_val = altera_unfreeze(region);
+ } else {
+ fpga_no_sup((char *)__func__, "Altera devices");
+ }
+ break;
+
default:
printf("%s: Invalid or unsupported device type %d\n",
__func__, desc->devtype);
diff --git a/include/altera.h b/include/altera.h
index 946413c66e8..610e9cba070 100644
--- a/include/altera.h
+++ b/include/altera.h
@@ -2,6 +2,8 @@
/*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland at enterasys.com.
+ *
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
*/
#include <fpga.h>
@@ -125,4 +127,9 @@ int intel_sdm_mb_load(Altera_desc *desc, const void *rbf_data,
size_t rbf_size);
#endif
+#ifdef CONFIG_FPGA_PR
+int altera_freeze(unsigned int region);
+int altera_unfreeze(unsigned int region);
+#endif
+
#endif /* _ALTERA_H_ */
--
2.35.3
More information about the U-Boot
mailing list