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.ravulapa...@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, rirel...@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, rirel...@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

Reply via email to