From 8df653c32545171ecede4b740e38e8a4af4ed9eb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 16 Apr 2019 23:05:24 +0200 Subject: [PATCH 1/9] ARM: socfpga: Factor out handoff register configuration Factor out the code for programming preloader handoff register values, the ISWGRP Handoff 0 and 1. These registers later control which bridges are enabled by the "bridge" command on Gen5 devices. Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen Cc: Simon Goldschmidt Cc: Tien Fong Chee --- .../include/mach/reset_manager_gen5.h | 1 + arch/arm/mach-socfpga/reset_manager_gen5.c | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-socfpga/include/mach/reset_manager_gen5.h b/arch/arm/mach-socfpga/include/mach/reset_manager_gen5.h index dd58922cec..5e490d182e 100644 --- a/arch/arm/mach-socfpga/include/mach/reset_manager_gen5.h +++ b/arch/arm/mach-socfpga/include/mach/reset_manager_gen5.h @@ -9,6 +9,7 @@ #include void reset_deassert_peripherals_handoff(void); +void socfpga_bridges_set_handoff_regs(bool h2f, bool lwh2f, bool f2h); void socfpga_bridges_reset(int enable); struct socfpga_reset_manager { diff --git a/arch/arm/mach-socfpga/reset_manager_gen5.c b/arch/arm/mach-socfpga/reset_manager_gen5.c index 25baef79bc..66af924485 100644 --- a/arch/arm/mach-socfpga/reset_manager_gen5.c +++ b/arch/arm/mach-socfpga/reset_manager_gen5.c @@ -73,6 +73,28 @@ void reset_deassert_peripherals_handoff(void) #define L3REGS_REMAP_HPS2FPGA_MASK 0x08 #define L3REGS_REMAP_OCRAM_MASK 0x01 +void socfpga_bridges_set_handoff_regs(bool h2f, bool lwh2f, bool f2h) +{ + u32 brgmask = 0x0; + u32 l3rmask = L3REGS_REMAP_OCRAM_MASK; + + if (h2f) + brgmask |= BIT(0); + else + l3rmask |= L3REGS_REMAP_HPS2FPGA_MASK; + + if (lwh2f) + brgmask |= BIT(1); + else + l3rmask |= L3REGS_REMAP_LWHPS2FPGA_MASK; + + if (f2h) + brgmask |= BIT(2); + + writel(brgmask, &sysmgr_regs->iswgrp_handoff[0]); + writel(l3rmask, &sysmgr_regs->iswgrp_handoff[1]); +} + void socfpga_bridges_reset(int enable) { const u32 l3mask = L3REGS_REMAP_LWHPS2FPGA_MASK | @@ -83,8 +105,7 @@ void socfpga_bridges_reset(int enable) /* brdmodrst */ writel(0xffffffff, &reset_manager_base->brg_mod_reset); } else { - writel(0, &sysmgr_regs->iswgrp_handoff[0]); - writel(l3mask, &sysmgr_regs->iswgrp_handoff[1]); + socfpga_bridges_set_handoff_regs(false, false, false); /* Check signal from FPGA. */ if (!fpgamgr_test_fpga_ready()) { From c1d4b464c8b8826b1d8a6d84ee5202f71ce933d1 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 16 Apr 2019 14:19:34 +0200 Subject: [PATCH 2/9] ARM: socfpga: Disable bridges in SPL unless booting from FPGA Disable bridges between L3 Main switch and FPGA unless booting from FPGA and keep them disabled to prevent glitches and possible hangs of the L3 Main switch. The current version of the code could have enabled the bridges between the L3 Main switch and FPGA for a short period of time in board_init_f() in case the FPGA was programmed and then again disable them at the end of board_init_f(). Replace this with a code which only sets up the handoff registers and let the user enable the bridges later on. Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen Cc: Simon Goldschmidt Cc: Tien Fong Chee --- arch/arm/mach-socfpga/spl_gen5.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/spl_gen5.c index 9dd0afb4bc..bd2a9fe5ae 100644 --- a/arch/arm/mach-socfpga/spl_gen5.c +++ b/arch/arm/mach-socfpga/spl_gen5.c @@ -175,8 +175,9 @@ void board_init_f(ulong dummy) sysmgr_pinmux_init(); sysmgr_config_warmrstcfgio(0); - /* De-assert reset for bridges based on handoff */ - socfpga_bridges_reset(0); + /* De-assert reset for peripherals and bridges based on handoff */ + reset_deassert_peripherals_handoff(); + socfpga_bridges_set_handoff_regs(true, true, true); debug("Unfreezing/Thaw all I/O banks\n"); /* unfreeze / thaw all IO banks */ @@ -205,7 +206,4 @@ void board_init_f(ulong dummy) debug("DRAM init failed: %d\n", ret); hang(); } - - if (!socfpga_is_booting_from_fpga()) - socfpga_bridges_reset(1); } From ba2cfcee889db2de099c46a60090624edf202a81 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 16 Apr 2019 22:13:29 +0200 Subject: [PATCH 3/9] ARM: socfpga: Fully unmap the FPGA bridges from L3 space Instead of just putting the bridges into reset, fully remove the bridges from the L3 main bridge space when disabling them by clearing bits in NIC-301 remap register. Moreover, only touch the 3 LSbits in brgmodrst register as the rest of the bits are undefined. Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen Cc: Simon Goldschmidt Cc: Tien Fong Chee --- arch/arm/mach-socfpga/reset_manager_gen5.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-socfpga/reset_manager_gen5.c b/arch/arm/mach-socfpga/reset_manager_gen5.c index 66af924485..89a384b59c 100644 --- a/arch/arm/mach-socfpga/reset_manager_gen5.c +++ b/arch/arm/mach-socfpga/reset_manager_gen5.c @@ -103,7 +103,8 @@ void socfpga_bridges_reset(int enable) if (enable) { /* brdmodrst */ - writel(0xffffffff, &reset_manager_base->brg_mod_reset); + writel(0x7, &reset_manager_base->brg_mod_reset); + writel(L3REGS_REMAP_OCRAM_MASK, SOCFPGA_L3REGS_ADDRESS); } else { socfpga_bridges_set_handoff_regs(false, false, false); From 72c347ced8c09f8a61452c884e992c444f555fe2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 16 Apr 2019 22:28:08 +0200 Subject: [PATCH 4/9] ARM: socfpga: Add support for selecting bridges in bridge command Add optional "mask" argument to the SoCFPGA bridge command, to select which bridges should be enabled/disabled. This allows the user to avoid enabling bridges which are not connected into the FPGA fabric. Default behavior is to enable/disable all bridges. Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen Cc: Simon Goldschmidt Cc: Tien Fong Chee --- arch/arm/mach-socfpga/include/mach/misc.h | 2 +- arch/arm/mach-socfpga/misc.c | 17 +++++++++++------ arch/arm/mach-socfpga/misc_arria10.c | 2 +- arch/arm/mach-socfpga/misc_gen5.c | 12 +++++++++++- arch/arm/mach-socfpga/misc_s10.c | 2 +- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-socfpga/include/mach/misc.h b/arch/arm/mach-socfpga/include/mach/misc.h index 86d5d2b62b..c3ca8cdf3b 100644 --- a/arch/arm/mach-socfpga/include/mach/misc.h +++ b/arch/arm/mach-socfpga/include/mach/misc.h @@ -39,6 +39,6 @@ void socfpga_init_security_policies(void); void socfpga_sdram_remap_zero(void); #endif -void do_bridge_reset(int enable); +void do_bridge_reset(int enable, unsigned int mask); #endif /* _MISC_H_ */ diff --git a/arch/arm/mach-socfpga/misc.c b/arch/arm/mach-socfpga/misc.c index ec8339e045..e1ea8eb73e 100644 --- a/arch/arm/mach-socfpga/misc.c +++ b/arch/arm/mach-socfpga/misc.c @@ -126,17 +126,22 @@ int arch_cpu_init(void) #ifndef CONFIG_SPL_BUILD static int do_bridge(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - if (argc != 2) + unsigned int mask = ~0; + + if (argc < 2 || argc > 3) return CMD_RET_USAGE; argv++; + if (argc == 3) + mask = simple_strtoul(argv[1], NULL, 16); + switch (*argv[0]) { case 'e': /* Enable */ - do_bridge_reset(1); + do_bridge_reset(1, mask); break; case 'd': /* Disable */ - do_bridge_reset(0); + do_bridge_reset(0, mask); break; default: return CMD_RET_USAGE; @@ -145,10 +150,10 @@ static int do_bridge(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 0; } -U_BOOT_CMD(bridge, 2, 1, do_bridge, +U_BOOT_CMD(bridge, 3, 1, do_bridge, "SoCFPGA HPS FPGA bridge control", - "enable - Enable HPS-to-FPGA, FPGA-to-HPS, LWHPS-to-FPGA bridges\n" - "bridge disable - Enable HPS-to-FPGA, FPGA-to-HPS, LWHPS-to-FPGA bridges\n" + "enable [mask] - Enable HPS-to-FPGA, FPGA-to-HPS, LWHPS-to-FPGA bridges\n" + "bridge disable [mask] - Enable HPS-to-FPGA, FPGA-to-HPS, LWHPS-to-FPGA bridges\n" "" ); diff --git a/arch/arm/mach-socfpga/misc_arria10.c b/arch/arm/mach-socfpga/misc_arria10.c index 63b8c75d31..2e2a40b65d 100644 --- a/arch/arm/mach-socfpga/misc_arria10.c +++ b/arch/arm/mach-socfpga/misc_arria10.c @@ -115,7 +115,7 @@ int print_cpuinfo(void) } #endif -void do_bridge_reset(int enable) +void do_bridge_reset(int enable, unsigned int mask) { if (enable) socfpga_reset_deassert_bridges_handoff(); diff --git a/arch/arm/mach-socfpga/misc_gen5.c b/arch/arm/mach-socfpga/misc_gen5.c index 9865f5b5b1..dda95e8237 100644 --- a/arch/arm/mach-socfpga/misc_gen5.c +++ b/arch/arm/mach-socfpga/misc_gen5.c @@ -239,9 +239,19 @@ static void socfpga_sdram_apply_static_cfg(void) : : "r"(val), "r"(&sdr_ctrl->static_cfg) : "memory", "cc"); } -void do_bridge_reset(int enable) +void do_bridge_reset(int enable, unsigned int mask) { + int i; + if (enable) { + socfpga_bridges_set_handoff_regs(!(mask & BIT(0)), + !(mask & BIT(1)), + !(mask & BIT(2))); + for (i = 0; i < 2; i++) { /* Reload SW setting cache */ + iswgrp_handoff[i] = + readl(&sysmgr_regs->iswgrp_handoff[i]); + } + writel(iswgrp_handoff[2], &sysmgr_regs->fpgaintfgrp_module); socfpga_sdram_apply_static_cfg(); writel(iswgrp_handoff[3], &sdr_ctrl->fpgaport_rst); diff --git a/arch/arm/mach-socfpga/misc_s10.c b/arch/arm/mach-socfpga/misc_s10.c index 113eace650..60c96090ce 100644 --- a/arch/arm/mach-socfpga/misc_s10.c +++ b/arch/arm/mach-socfpga/misc_s10.c @@ -150,7 +150,7 @@ int arch_early_init_r(void) return 0; } -void do_bridge_reset(int enable) +void do_bridge_reset(int enable, unsigned int mask) { socfpga_bridges_reset(enable); } From 4e16f0a67d80b4ce11995b870b5d9c8d11266d0d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 23 Mar 2019 03:32:24 +0100 Subject: [PATCH 5/9] mmc: dw_mmc: Calculate timeout from transfer length The current 4-minute data transfer timeout is misleading and broken. Instead of such a long wait, calculate the timeout duration based on the length of the data transfer. The current formula is the transfer length in bits, divided by a multiplication of bus frequency in Hz, bus width, DDR mode and converted the mSec. The value is bounded from the bottom to 1000 mSec. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Simon Glass --- drivers/mmc/dw_mmc.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 93a836eac3..d4976ac879 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -114,22 +114,40 @@ static int dwmci_fifo_ready(struct dwmci_host *host, u32 bit, u32 *len) return 0; } +static unsigned int dwmci_get_timeout(struct mmc *mmc, const unsigned int size) +{ + unsigned int timeout; + + timeout = size * 8 * 1000; /* counting in bits and msec */ + timeout *= 2; /* wait twice as long */ + timeout /= mmc->clock; + timeout /= mmc->bus_width; + timeout /= mmc->ddr_mode ? 2 : 1; + timeout = (timeout < 1000) ? 1000 : timeout; + + return timeout; +} + static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) { + struct mmc *mmc = host->mmc; int ret = 0; - u32 timeout = 240000; - u32 mask, size, i, len = 0; + u32 timeout, mask, size, i, len = 0; u32 *buf = NULL; ulong start = get_timer(0); u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1) * 2; - size = data->blocksize * data->blocks / 4; + size = data->blocksize * data->blocks; if (data->flags == MMC_DATA_READ) buf = (unsigned int *)data->dest; else buf = (unsigned int *)data->src; + timeout = dwmci_get_timeout(mmc, size); + + size /= 4; + for (;;) { mask = dwmci_readl(host, DWMCI_RINTSTS); /* Error during data transfer. */ From 6ad5aec419782955fa4be79c93f2640fc043f3cf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 23 Mar 2019 18:45:27 +0100 Subject: [PATCH 6/9] mmc: dw_mmc: Handle return value from bounce_buffer_start() The bounce_buffer_start() can return -ENOMEM in case memory allocation failed. However, in that case, the bounce buffer address is the same as the possibly unaligned input address, and the cache maintenance operations were not applied to this address. This could cause subtle problems. Add handling for the bounce_buffer_start() return value to prevent such a problem from happening. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Simon Glass --- drivers/mmc/dw_mmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index d4976ac879..2faaa43911 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -270,14 +270,20 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); } else { if (data->flags == MMC_DATA_READ) { - bounce_buffer_start(&bbstate, (void*)data->dest, + ret = bounce_buffer_start(&bbstate, + (void*)data->dest, data->blocksize * data->blocks, GEN_BB_WRITE); } else { - bounce_buffer_start(&bbstate, (void*)data->src, + ret = bounce_buffer_start(&bbstate, + (void*)data->src, data->blocksize * data->blocks, GEN_BB_READ); } + + if (ret) + return ret; + dwmci_prepare_data(host, data, cur_idmac, bbstate.bounce_buffer); } From bdb5df1a06777c3363b4579f5370f852635cb977 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 13 Feb 2019 20:16:20 +0100 Subject: [PATCH 7/9] mmc: dw_mmc: Round up descriptor end to nearest multiple of cacheline size The driver currently calculates the end address of cache flush operation for the DMA descriptors by adding cacheline size to the start address of the last DMA descriptor. This is not safe, as the cacheline size may be, in some unlikely cases, smaller than the DMA descriptor size. Replace the addition with roundup() applied on the end address of the last DMA descriptor to round it up to the nearest cacheline size multiple. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Simon Glass --- drivers/mmc/dw_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 2faaa43911..1992d61182 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -74,15 +74,15 @@ static void dwmci_prepare_data(struct dwmci_host *host, dwmci_set_idma_desc(cur_idmac, flags, cnt, (ulong)bounce_buffer + (i * PAGE_SIZE)); + cur_idmac++; if (blk_cnt <= 8) break; blk_cnt -= 8; - cur_idmac++; i++; } while(1); data_end = (ulong)cur_idmac; - flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); + flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; From c5f4b805755912a3d2fe20f014b6b6ab0473bd73 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 23 Apr 2019 17:24:22 +0200 Subject: [PATCH 8/9] ARM: socfpga: Remove socfpga_sdram_apply_static_cfg() The usage of socfpga_sdram_apply_static_cfg() seems rather dubious and is confirmed to lead to a rare system hang when enabling bridges. This patch removes the socfpga_sdram_apply_static_cfg() altogether, because it's use seems unjustified and problematic. The socfpga_sdram_apply_static_cfg() triggers write to SDRAM staticcfg register to set the applycfg bit, which according to old vendor U-Boot sources can only be written when there is no traffic between the SDRAM controller and the rest of the system. Empirical measurements confirm this, setting the applycfg bit when there is traffic between the SDRAM controller and CPU leads to the SDRAM controller accesses being blocked shortly after. Altera originally solved this by moving the entire code which sets the staticcfg register to OCRAM [1]. The commit message claims that the applycfg bit needs to be set after write to fpgaportrst register. This is however inverted by Altera shortly after in [2], where the order becomes the exact opposite of what commit message [1] claims to be the required order. The explanation points to a possible problem in AMP use-case, where the FPGA might be sending transactions through the F2S bridge. However, the AMP is only the tip of the iceberg here. Any of the other L2, L3 or L4 masters can trigger transactions to the SDRAM. It becomes rather non-trivial to guarantee there are no transactions to the SDRAM controller. The SoCFPGA SDRAM driver always writes the applycfg bit in SPL. Thus, writing the applycfg again in bridge enable code seems redundant and can presumably be dropped. [1] https://github.com/altera-opensource/u-boot-socfpga/commit/75905816ec95b0ccd515700b922628d7aa9036f8 [2] https://github.com/altera-opensource/u-boot-socfpga/commit/8ba6986b04a91d23c7adf529186b34c8d2967ad5 Signed-off-by: Marek Vasut Cc: Chin Liang See Cc: Dinh Nguyen Cc: Simon Goldschmidt Cc: Tien Fong Chee --- arch/arm/mach-socfpga/misc_gen5.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/arch/arm/mach-socfpga/misc_gen5.c b/arch/arm/mach-socfpga/misc_gen5.c index dda95e8237..71547d81ab 100644 --- a/arch/arm/mach-socfpga/misc_gen5.c +++ b/arch/arm/mach-socfpga/misc_gen5.c @@ -210,35 +210,6 @@ static struct socfpga_reset_manager *reset_manager_base = static struct socfpga_sdr_ctrl *sdr_ctrl = (struct socfpga_sdr_ctrl *)SDR_CTRLGRP_ADDRESS; -static void socfpga_sdram_apply_static_cfg(void) -{ - const u32 applymask = 0x8; - u32 val = readl(&sdr_ctrl->static_cfg) | applymask; - - /* - * SDRAM staticcfg register specific: - * When applying the register setting, the CPU must not access - * SDRAM. Luckily for us, we can abuse i-cache here to help us - * circumvent the SDRAM access issue. The idea is to make sure - * that the code is in one full i-cache line by branching past - * it and back. Once it is in the i-cache, we execute the core - * of the code and apply the register settings. - * - * The code below uses 7 instructions, while the Cortex-A9 has - * 32-byte cachelines, thus the limit is 8 instructions total. - */ - asm volatile( - ".align 5 \n" - " b 2f \n" - "1: str %0, [%1] \n" - " dsb \n" - " isb \n" - " b 3f \n" - "2: b 1b \n" - "3: nop \n" - : : "r"(val), "r"(&sdr_ctrl->static_cfg) : "memory", "cc"); -} - void do_bridge_reset(int enable, unsigned int mask) { int i; @@ -253,14 +224,12 @@ void do_bridge_reset(int enable, unsigned int mask) } writel(iswgrp_handoff[2], &sysmgr_regs->fpgaintfgrp_module); - socfpga_sdram_apply_static_cfg(); writel(iswgrp_handoff[3], &sdr_ctrl->fpgaport_rst); writel(iswgrp_handoff[0], &reset_manager_base->brg_mod_reset); writel(iswgrp_handoff[1], &nic301_regs->remap); } else { writel(0, &sysmgr_regs->fpgaintfgrp_module); writel(0, &sdr_ctrl->fpgaport_rst); - socfpga_sdram_apply_static_cfg(); writel(0, &reset_manager_base->brg_mod_reset); writel(1, &nic301_regs->remap); } From 7110259f550ce2c300f6f2c1760576c180705f4e Mon Sep 17 00:00:00 2001 From: Simon Goldschmidt Date: Mon, 29 Apr 2019 20:32:27 +0200 Subject: [PATCH 9/9] dts: arm: socfpga: fix socfpga_de10_nano console Booting this board failed as the initial console isn't found since commit c402e8170245 ("dts: arm: socfpga: merge gen5 devicetrees from linux") The uart0 devicetree entry was missing "clock-frequency = <100000000>:" since that commit Fixes: c402e8170245 ("dts: arm: socfpga: merge gen5 devicetrees from linux") Reported-by: rafael mello Signed-off-by: Simon Goldschmidt --- arch/arm/dts/socfpga_cyclone5_de10_nano.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/dts/socfpga_cyclone5_de10_nano.dts b/arch/arm/dts/socfpga_cyclone5_de10_nano.dts index b620dd8dda..4be4083941 100644 --- a/arch/arm/dts/socfpga_cyclone5_de10_nano.dts +++ b/arch/arm/dts/socfpga_cyclone5_de10_nano.dts @@ -77,6 +77,7 @@ }; &uart0 { + clock-frequency = <100000000>; u-boot,dm-pre-reloc; };