| /* |
| * Copyright (C) 2012 Altera Corporation <www.altera.com> |
| * All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <common.h> |
| #include <asm/io.h> |
| #include <asm/errno.h> |
| #include <asm/arch/fpga_manager.h> |
| #include <asm/arch/reset_manager.h> |
| #include <asm/arch/system_manager.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| /* Timeout count */ |
| #define FPGA_TIMEOUT_CNT 0x1000000 |
| |
| static struct socfpga_fpga_manager *fpgamgr_regs = |
| (struct socfpga_fpga_manager *)SOCFPGA_FPGAMGRREGS_ADDRESS; |
| static struct socfpga_system_manager *sysmgr_regs = |
| (struct socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; |
| |
| /* Set CD ratio */ |
| static void fpgamgr_set_cd_ratio(unsigned long ratio) |
| { |
| clrsetbits_le32(&fpgamgr_regs->ctrl, |
| 0x3 << FPGAMGRREGS_CTRL_CDRATIO_LSB, |
| (ratio & 0x3) << FPGAMGRREGS_CTRL_CDRATIO_LSB); |
| } |
| |
| static int fpgamgr_dclkcnt_set(unsigned long cnt) |
| { |
| unsigned long i; |
| |
| /* Clear any existing done status */ |
| if (readl(&fpgamgr_regs->dclkstat)) |
| writel(0x1, &fpgamgr_regs->dclkstat); |
| |
| /* Write the dclkcnt */ |
| writel(cnt, &fpgamgr_regs->dclkcnt); |
| |
| /* Wait till the dclkcnt done */ |
| for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { |
| if (!readl(&fpgamgr_regs->dclkstat)) |
| continue; |
| |
| writel(0x1, &fpgamgr_regs->dclkstat); |
| return 0; |
| } |
| |
| return -ETIMEDOUT; |
| } |
| |
| /* Start the FPGA programming by initialize the FPGA Manager */ |
| static int fpgamgr_program_init(void) |
| { |
| unsigned long msel, i; |
| |
| /* Get the MSEL value */ |
| msel = readl(&fpgamgr_regs->stat); |
| msel &= FPGAMGRREGS_STAT_MSEL_MASK; |
| msel >>= FPGAMGRREGS_STAT_MSEL_LSB; |
| |
| /* |
| * Set the cfg width |
| * If MSEL[3] = 1, cfg width = 32 bit |
| */ |
| if (msel & 0x8) { |
| setbits_le32(&fpgamgr_regs->ctrl, |
| FPGAMGRREGS_CTRL_CFGWDTH_MASK); |
| |
| /* To determine the CD ratio */ |
| /* MSEL[1:0] = 0, CD Ratio = 1 */ |
| if ((msel & 0x3) == 0x0) |
| fpgamgr_set_cd_ratio(CDRATIO_x1); |
| /* MSEL[1:0] = 1, CD Ratio = 4 */ |
| else if ((msel & 0x3) == 0x1) |
| fpgamgr_set_cd_ratio(CDRATIO_x4); |
| /* MSEL[1:0] = 2, CD Ratio = 8 */ |
| else if ((msel & 0x3) == 0x2) |
| fpgamgr_set_cd_ratio(CDRATIO_x8); |
| |
| } else { /* MSEL[3] = 0 */ |
| clrbits_le32(&fpgamgr_regs->ctrl, |
| FPGAMGRREGS_CTRL_CFGWDTH_MASK); |
| |
| /* To determine the CD ratio */ |
| /* MSEL[1:0] = 0, CD Ratio = 1 */ |
| if ((msel & 0x3) == 0x0) |
| fpgamgr_set_cd_ratio(CDRATIO_x1); |
| /* MSEL[1:0] = 1, CD Ratio = 2 */ |
| else if ((msel & 0x3) == 0x1) |
| fpgamgr_set_cd_ratio(CDRATIO_x2); |
| /* MSEL[1:0] = 2, CD Ratio = 4 */ |
| else if ((msel & 0x3) == 0x2) |
| fpgamgr_set_cd_ratio(CDRATIO_x4); |
| } |
| |
| /* To enable FPGA Manager configuration */ |
| clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCE_MASK); |
| |
| /* To enable FPGA Manager drive over configuration line */ |
| setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK); |
| |
| /* Put FPGA into reset phase */ |
| setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK); |
| |
| /* (1) wait until FPGA enter reset phase */ |
| for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { |
| if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_RESETPHASE) |
| break; |
| } |
| |
| /* If not in reset state, return error */ |
| if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_RESETPHASE) { |
| puts("FPGA: Could not reset\n"); |
| return -1; |
| } |
| |
| /* Release FPGA from reset phase */ |
| clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK); |
| |
| /* (2) wait until FPGA enter configuration phase */ |
| for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { |
| if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_CFGPHASE) |
| break; |
| } |
| |
| /* If not in configuration state, return error */ |
| if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_CFGPHASE) { |
| puts("FPGA: Could not configure\n"); |
| return -2; |
| } |
| |
| /* Clear all interrupts in CB Monitor */ |
| writel(0xFFF, &fpgamgr_regs->gpio_porta_eoi); |
| |
| /* Enable AXI configuration */ |
| setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK); |
| |
| return 0; |
| } |
| |
| /* Write the RBF data to FPGA Manager */ |
| static void fpgamgr_program_write(const void *rbf_data, unsigned long rbf_size) |
| { |
| uint32_t src = (uint32_t)rbf_data; |
| uint32_t dst = SOCFPGA_FPGAMGRDATA_ADDRESS; |
| |
| /* Number of loops for 32-byte long copying. */ |
| uint32_t loops32 = rbf_size / 32; |
| /* Number of loops for 4-byte long copying + trailing bytes */ |
| uint32_t loops4 = DIV_ROUND_UP(rbf_size % 32, 4); |
| |
| asm volatile( |
| "1: ldmia %0!, {r0-r7}\n" |
| " stmia %1!, {r0-r7}\n" |
| " sub %1, #32\n" |
| " subs %2, #1\n" |
| " bne 1b\n" |
| " cmp %3, #0\n" |
| " beq 3f\n" |
| "2: ldr %2, [%0], #4\n" |
| " str %2, [%1]\n" |
| " subs %3, #1\n" |
| " bne 2b\n" |
| "3: nop\n" |
| : "+r"(src), "+r"(dst), "+r"(loops32), "+r"(loops4) : |
| : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "cc"); |
| } |
| |
| /* Ensure the FPGA entering config done */ |
| static int fpgamgr_program_poll_cd(void) |
| { |
| const uint32_t mask = FPGAMGRREGS_MON_GPIO_EXT_PORTA_NS_MASK | |
| FPGAMGRREGS_MON_GPIO_EXT_PORTA_CD_MASK; |
| unsigned long reg, i; |
| |
| /* (3) wait until full config done */ |
| for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { |
| reg = readl(&fpgamgr_regs->gpio_ext_porta); |
| |
| /* Config error */ |
| if (!(reg & mask)) { |
| printf("FPGA: Configuration error.\n"); |
| return -3; |
| } |
| |
| /* Config done without error */ |
| if (reg & mask) |
| break; |
| } |
| |
| /* Timeout happened, return error */ |
| if (i == FPGA_TIMEOUT_CNT) { |
| printf("FPGA: Timeout waiting for program.\n"); |
| return -4; |
| } |
| |
| /* Disable AXI configuration */ |
| clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK); |
| |
| return 0; |
| } |
| |
| /* Ensure the FPGA entering init phase */ |
| static int fpgamgr_program_poll_initphase(void) |
| { |
| unsigned long i; |
| |
| /* Additional clocks for the CB to enter initialization phase */ |
| if (fpgamgr_dclkcnt_set(0x4)) |
| return -5; |
| |
| /* (4) wait until FPGA enter init phase or user mode */ |
| for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { |
| if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_INITPHASE) |
| break; |
| if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE) |
| break; |
| } |
| |
| /* If not in configuration state, return error */ |
| if (i == FPGA_TIMEOUT_CNT) |
| return -6; |
| |
| return 0; |
| } |
| |
| /* Ensure the FPGA entering user mode */ |
| static int fpgamgr_program_poll_usermode(void) |
| { |
| unsigned long i; |
| |
| /* Additional clocks for the CB to exit initialization phase */ |
| if (fpgamgr_dclkcnt_set(0x5000)) |
| return -7; |
| |
| /* (5) wait until FPGA enter user mode */ |
| for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { |
| if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE) |
| break; |
| } |
| /* If not in configuration state, return error */ |
| if (i == FPGA_TIMEOUT_CNT) |
| return -8; |
| |
| /* To release FPGA Manager drive over configuration line */ |
| clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK); |
| |
| return 0; |
| } |
| |
| /* |
| * FPGA Manager to program the FPGA. This is the interface used by FPGA driver. |
| * Return 0 for sucess, non-zero for error. |
| */ |
| int socfpga_load(Altera_desc *desc, const void *rbf_data, size_t rbf_size) |
| { |
| unsigned long status; |
| |
| if ((uint32_t)rbf_data & 0x3) { |
| puts("FPGA: Unaligned data, realign to 32bit boundary.\n"); |
| return -EINVAL; |
| } |
| |
| /* Prior programming the FPGA, all bridges need to be shut off */ |
| |
| /* Disable all signals from hps peripheral controller to fpga */ |
| writel(0, &sysmgr_regs->fpgaintfgrp_gbl); |
| |
| /* Disable all signals from FPGA to HPS SDRAM */ |
| #define SDR_CTRLGRP_FPGAPORTRST_ADDRESS 0x5080 |
| writel(0, SOCFPGA_SDR_ADDRESS + SDR_CTRLGRP_FPGAPORTRST_ADDRESS); |
| |
| /* Disable all axi bridge (hps2fpga, lwhps2fpga & fpga2hps) */ |
| socfpga_bridges_reset(1); |
| |
| /* Unmap the bridges from NIC-301 */ |
| writel(0x1, SOCFPGA_L3REGS_ADDRESS); |
| |
| /* Initialize the FPGA Manager */ |
| status = fpgamgr_program_init(); |
| if (status) |
| return status; |
| |
| /* Write the RBF data to FPGA Manager */ |
| fpgamgr_program_write(rbf_data, rbf_size); |
| |
| /* Ensure the FPGA entering config done */ |
| status = fpgamgr_program_poll_cd(); |
| if (status) |
| return status; |
| |
| /* Ensure the FPGA entering init phase */ |
| status = fpgamgr_program_poll_initphase(); |
| if (status) |
| return status; |
| |
| /* Ensure the FPGA entering user mode */ |
| return fpgamgr_program_poll_usermode(); |
| } |