| /* | |
| * 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 <linux/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_module); | |
| /* 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(); | |
| } |