From 230fe9b202ff0ca396ad9a564816cc87d42daa6e Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 8 Sep 2014 14:08:45 +0200 Subject: arm: socfpga: fpga: Add SoCFPGA FPGA programming interface Add code necessary to program the FPGA part of SoCFPGA from U-Boot with an RBF blob. This patch also integrates the code into the FPGA driver framework in U-Boot so it can be used via the 'fpga' command. Signed-off-by: Pavel Machek Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen Cc: Albert Aribaud Cc: Tom Rini Cc: Wolfgang Denk Cc: Pavel Machek V2: Move the not-CPU specific stuff into drivers/fpga/ and base this on the cleaned up altera FPGA support. --- drivers/fpga/socfpga.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 drivers/fpga/socfpga.c (limited to 'drivers/fpga/socfpga.c') diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c new file mode 100644 index 0000000..63b3566 --- /dev/null +++ b/drivers/fpga/socfpga.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2012 Altera Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +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" + "2: ldr %2, [%0], #4\n" + " str %2, [%1]\n" + " subs %3, #1\n" + " bne 2b\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(); +} -- cgit v1.1