Adding Partial Reconfiguration (pr) command at U-Boot console. The pr
command will control the Freeze Controller which will freeze
and unfreeze the Partial Reconfiguration region. The pr command also
support multiple Partial Reconfiguration region by specifying the
region ID.

Here are the example of expected steps for partial reconfiguration
$ bridge enable
$ fatload mmc 0:1 100 pr_bitstream.rbf
$ pr start 0
$ fpga load 0 100 $filesize
$ pr end 0

Signed-off-by: Boon Khai Ng <[email protected]>
---
 drivers/fpga/Kconfig     |   9 ++
 drivers/fpga/Makefile    |   1 +
 drivers/fpga/altera_pr.c | 188 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 198 insertions(+)
 create mode 100644 drivers/fpga/altera_pr.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 1658c73bca4..82f887128f1 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -55,6 +55,15 @@ config FPGA_INTEL_SDM_MAILBOX
          Enable FPGA driver for writing full bitstream into Intel FPGA
          devices through SDM (Secure Device Manager) Mailbox.
 
+config FPGA_ALTERA_PR
+       bool "Enable Altera FPGA Partial Reconfiguration driver"
+       depends on FPGA_ALTERA
+       help
+         Say Y here to enable the Altera FPGA Partial Reconfiguration driver
+
+         This provides basic functionality for partial reconfiguration which
+         include the freeze controller support.
+
 config FPGA_XILINX
        bool "Enable Xilinx FPGA drivers"
        select FPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index ccfed94717e..1e1011e9da8 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_FPGA_STRATIX_V) += stratixv.o
 obj-$(CONFIG_FPGA_SOCFPGA) += socfpga.o
 obj-$(CONFIG_ARCH_SOCFPGA_GEN5) += socfpga_gen5.o
 obj-$(CONFIG_ARCH_SOCFPGA_ARRIA10) += socfpga_arria10.o
+obj-$(CONFIG_FPGA_ALTERA_PR) += altera_pr.o
 endif
diff --git a/drivers/fpga/altera_pr.c b/drivers/fpga/altera_pr.c
new file mode 100644
index 00000000000..7ac39c79c45
--- /dev/null
+++ b/drivers/fpga/altera_pr.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Altera Corporation <www.altera.com>
+ */
+
+#include <command.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_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)
+
+#define FREEZE_TIMEOUT                         20000
+
+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 %x\n",
+                              __func__, req_ack, status, ctrl);
+
+                       ret = 0;
+                       break;
+               }
+
+               if (get_timer(start) > FREEZE_TIMEOUT)
+                       break;
+
+               udelay(1);
+               schedule();
+       }
+
+       return ret;
+}
+
+static int altera_freeze_br_do_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;
+       else 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);
+       else
+               writel(FREEZE_CSR_CTRL_RESET_REQ, addr + 
FREEZE_CSR_CTRL_OFFSET);
+
+       return ret;
+}
+
+static int altera_freeze_br_do_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;
+       else 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;
+}
+
+static int do_pr(struct cmd_tbl *cmdtp, int flag, int argc, char * const 
argv[])
+{
+       const char *cmd;
+       char *region;
+       unsigned int region_num = 0;
+
+       if (argc != 2 && argc != 3)
+               return CMD_RET_USAGE;
+
+       cmd = argv[1];
+       if (argc == 3)
+               region_num = simple_strtoul(argv[2], &region, 0);
+
+       if (strcmp(cmd, "start") == 0)
+               return altera_freeze_br_do_freeze(region_num);
+       else if (strcmp(cmd, "end") == 0)
+               return altera_freeze_br_do_unfreeze(region_num);
+
+       return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(pr, 3, 1, do_pr,
+          "SoCFPGA partial reconfiguration control",
+          "start [region] - Start the partial reconfiguration by freeze the PR 
region\n"
+          "end [region] - End the partial reconfiguration by unfreeze the PR 
region\n"
+          ""
+);
-- 
2.43.7

Reply via email to