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