summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVince Bridgers <vbridger@opensource.altera.com>2014-09-23 09:39:57 -0500
committerVince Bridgers <vbridger@opensource.altera.com>2014-10-08 16:59:00 -0500
commit93815696dce132ff8abc4ab2f4c195339ff821a0 (patch)
tree7f8680857bbc001136b36928df5f68d3b8a5a154
parenta494dcaa326dbdb195376ed68efcb8534c0ad86c (diff)
downloadu-boot-socfpga-93815696dce132ff8abc4ab2f4c195339ff821a0.tar.gz
This patch enhances the previously submitted patch for FogBugz #223741 by programming the SDRAM protection rules to prevent SDRAM accesses beyond maximum configured SDRAM. This patch also corrects an issue in the way SDRAM rows was calculated and configured in that same patch. The number of rows is set such that 4G of memory is decoded to workaround the SDRAM errata and limits memory accesses by using the SDRAM range protection logic to cause a data abort if an access occurs beyond available, configured memory in the system. Signed-off-by: Vince Bridgers <vbridger@opensource.altera.com>
-rw-r--r--arch/arm/cpu/armv7/socfpga/altlimits.h130
-rw-r--r--arch/arm/cpu/armv7/socfpga/sdram.c274
-rw-r--r--arch/arm/include/asm/arch-socfpga/sdram.h10
-rw-r--r--arch/arm/include/asm/arch-socfpga/system_manager.h1
-rwxr-xr-xdoc/README.SOCFPGA2
5 files changed, 412 insertions, 5 deletions
diff --git a/arch/arm/cpu/armv7/socfpga/altlimits.h b/arch/arm/cpu/armv7/socfpga/altlimits.h
new file mode 100644
index 0000000000..09d45e34fe
--- /dev/null
+++ b/arch/arm/cpu/armv7/socfpga/altlimits.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This is an abstract header file for best portability. uBoot does not
+ * have the equivalent of a limits.h file (Linux has kernel.h), and
+ * some recently added code requires range checks on computation results.
+ *
+ * This code first checks for the existence of the macro before defining it
+ * in the event limits.h or the equivalent is ever added in the future.
+ * This file can be removed if that ever occurs.
+ */
+
+#ifndef __ALTLIMITS_H__
+#define __ALTLIMITS_H__
+
+/*
+ * ANSI C - number of bits by type
+ */
+#ifndef CHAR_BIT
+ #define CHAR_BIT (8)
+#endif
+
+#ifndef BYTE_BIT
+ #define BYTE_BIT (8)
+#endif
+
+#ifndef SHORT_BIT
+ #define SHORT_BIT (16)
+#endif
+
+#ifndef INT_BIT
+ #define INT_BIT (32)
+#endif
+
+#ifndef BYTE_MAX
+ #define BYTE_MAX (255)
+#endif
+
+#ifndef SCHAR_MAX
+ #define SCHAR_MAX (127)
+ #define SCHAR_MIN (-SCHAR_MAX - 1)
+#endif
+
+#ifndef CHAR_MAX
+ #define CHAR_MAX (127)
+ #define CHAR_MIN (-CHAR_MAX - 1)
+#endif
+
+#ifndef UCHAR_MAX
+ #define UCHAR_MAX (0xff)
+#endif
+
+#ifndef SHRT_MAX
+ #define SHRT_MAX (32767)
+ #define SHRT_MIN (-SHRT_MAX - 1)
+#endif
+
+#ifndef USHRT_MAX
+ #define USHRT_MAX (0xffff)
+#endif
+
+#ifndef INT_MAX
+ #define INT_MAX (2147483647)
+ #define INT_MIN (-INT_MAX - 1)
+#endif
+
+#ifndef UINT_MAX
+ #define UINT_MAX (0xffffffff)
+#endif
+
+#ifndef LONG_MAX
+ #define LONG_MAX (INT_MAX)
+ #define LONG_MIN (-LONG_MAX - 1)
+#endif
+
+#ifndef ULONG_MAX
+ #define ULONG_MAX (UINT_MAX)
+#endif
+
+#ifndef INT8_MIN
+ #define INT8_MIN (SCHAR_MIN)
+#endif
+
+#ifndef INT8_MAX
+ #define INT8_MAX (SCHAR_MAX)
+#endif
+
+#ifndef UINT8_MAX
+ #define UINT8_MAX (UCHAR_MAX)
+#endif
+
+#ifndef INT16_MIN
+ #define INT16_MIN (SHRT_MIN)
+#endif
+
+#ifndef INT16_MAX
+ #define INT16_MAX (SHRT_MAX)
+#endif
+
+#ifndef UINT16_MAX
+ #define UINT16_MAX (USHRT_MAX)
+#endif
+
+#ifndef INT32_MIN
+ #define INT32_MIN (INT_MIN)
+#endif
+
+#ifndef INT32_MAX
+ #define INT32_MAX (INT_MAX)
+#endif
+
+#ifndef UINT32_MAX
+ #define UINT32_MAX (UINT_MAX)
+#endif
+
+#endif /* __ALTLIMITS_H__ */
+
+
diff --git a/arch/arm/cpu/armv7/socfpga/sdram.c b/arch/arm/cpu/armv7/socfpga/sdram.c
index 881348c73f..d2e2d1c6dc 100644
--- a/arch/arm/cpu/armv7/socfpga/sdram.c
+++ b/arch/arm/cpu/armv7/socfpga/sdram.c
@@ -21,9 +21,12 @@
#include <asm/arch/debug_memory.h>
#include <asm/arch/fpga_manager.h>
#include <asm/arch/system_manager.h>
+#include <div64.h>
#include <pl330.h>
#include <watchdog.h>
+#include "altlimits.h"
+
/*
* SDRAM MMR init skip read back/verify steps
* Define to speed up the MMR init process by just write without verifying
@@ -39,6 +42,9 @@
#define ADDRORDER0_INFO \
"INFO: Changing address order to 0 (chip, row, bank, column)\n"
+/* define constant for 4G memory - used for SDRAM errata workaround */
+#define MEMSIZE_4G (4ULL * 1024ULL * 1024ULL * 1024ULL)
+
DECLARE_GLOBAL_DATA_PTR;
unsigned long irq_cnt_ecc_sdram;
@@ -129,6 +135,226 @@ void irq_handler_ecc_sdram(void *arg)
/* Below function only applicable for SPL */
#ifdef CONFIG_SPL_BUILD
+int
+compute_errata_rows(unsigned long long memsize, int cs, int width,
+ int rows, int banks, int cols)
+{
+ unsigned long long newrows;
+ int inewrowslog2;
+ int bits;
+
+ debug("workaround rows - memsize %lld\n", memsize);
+ debug("workaround rows - cs %d\n", cs);
+ debug("workaround rows - width %d\n", width);
+ debug("workaround rows - rows %d\n", rows);
+ debug("workaround rows - banks %d\n", banks);
+ debug("workaround rows - cols %d\n", cols);
+
+ newrows = lldiv(memsize, (cs * (width / 8)));
+ debug("rows workaround - term1 %lld\n", newrows);
+
+ newrows = lldiv(newrows, ((1 << banks) * (1 << cols)));
+ debug("rows workaround - term2 %lld\n", newrows);
+
+ /* Compute the hamming weight - same as number of bits set.
+ * Need to see if result is ordinal power of 2 before
+ * attempting log2 of result.
+ */
+ bits = hweight32(newrows);
+
+ debug("rows workaround - bits %d\n", bits);
+
+ if (bits != 1) {
+ printf("SDRAM workaround failed, bits set %d\n", bits);
+ return rows;
+ }
+
+ if (newrows > UINT_MAX) {
+ printf("SDRAM workaround rangecheck failed, %lld\n", newrows);
+ return rows;
+ }
+
+ inewrowslog2 = __ilog2((unsigned int)newrows);
+
+ debug("rows workaround - ilog2 %d, %d\n", inewrowslog2,
+ (int)newrows);
+
+ if (inewrowslog2 == -1) {
+ printf("SDRAM workaround failed, newrows %d\n", (int)newrows);
+ return rows;
+ }
+
+ return inewrowslog2;
+}
+
+typedef struct _sdram_prot_rule {
+ uint32_t rule; /* SDRAM protection rule number: 0-19 */
+ uint64_t sdram_start; /* SDRAM start address */
+ uint64_t sdram_end; /* SDRAM end address */
+ int valid; /* Rule valid or not? 1 - valid, 0 not*/
+
+ uint32_t security;
+ uint32_t portmask;
+ uint32_t result;
+ uint32_t lo_prot_id;
+ uint32_t hi_prot_id;
+} sdram_prot_rule, *psdram_prot_rule;
+
+/* SDRAM protection rules vary from 0-19, a total of 20 rules. */
+
+void sdram_set_rule(psdram_prot_rule prule)
+{
+ int regoffs;
+ uint32_t lo_addr_bits;
+ uint32_t hi_addr_bits;
+ int ruleno = prule->rule;
+
+ /* Select the rule */
+ regoffs = SDR_CTRLGRP_PROTRULERDWR_ADDRESS;
+ writel(ruleno, (SOCFPGA_SDR_ADDRESS + regoffs));
+
+ /* Obtain the address bits */
+ lo_addr_bits = (uint32_t)(((prule->sdram_start) >> 20ULL) & 0xFFF);
+ hi_addr_bits = (uint32_t)((((prule->sdram_end-1) >> 20ULL)) & 0xFFF);
+
+ debug("sdram set rule start %x, %lld\n", lo_addr_bits,
+ prule->sdram_start);
+ debug("sdram set rule end %x, %lld\n", hi_addr_bits,
+ prule->sdram_end);
+
+ /* Set rule addresses */
+ regoffs = SDR_CTRLGRP_PROTRULEADDR_ADDRESS;
+ writel(lo_addr_bits | (hi_addr_bits << 12),
+ (SOCFPGA_SDR_ADDRESS + regoffs));
+
+ /* Set rule protection ids */
+ regoffs = SDR_CTRLGRP_PROTRULEID_ADDRESS;
+ writel(prule->lo_prot_id | (prule->hi_prot_id << 12),
+ (SOCFPGA_SDR_ADDRESS + regoffs));
+
+ /* Set the rule data */
+ regoffs = SDR_CTRLGRP_PROTRULEDATA_ADDRESS;
+ writel(prule->security | (prule->valid << 2) |
+ (prule->portmask << 3) | (prule->result << 13),
+ (SOCFPGA_SDR_ADDRESS + regoffs));
+
+ /* write the rule */
+ regoffs = SDR_CTRLGRP_PROTRULERDWR_ADDRESS;
+ writel(ruleno | (1L << 5),
+ (SOCFPGA_SDR_ADDRESS + regoffs));
+
+ /* Set rule number to 0 by default */
+ writel(0, (SOCFPGA_SDR_ADDRESS + regoffs));
+}
+
+void sdram_get_rule(psdram_prot_rule prule)
+{
+ int regoffs;
+ uint32_t protruleaddr;
+ uint32_t protruleid;
+ uint32_t protruledata;
+ int ruleno = prule->rule;
+
+ /* Read the rule */
+ regoffs = SDR_CTRLGRP_PROTRULERDWR_ADDRESS;
+ writel(ruleno, (SOCFPGA_SDR_ADDRESS + regoffs));
+ writel(ruleno | (1L << 6),
+ (SOCFPGA_SDR_ADDRESS + regoffs));
+
+ /* Get the addresses */
+ regoffs = SDR_CTRLGRP_PROTRULEADDR_ADDRESS;
+ protruleaddr = readl(SOCFPGA_SDR_ADDRESS + regoffs);
+ prule->sdram_start = (protruleaddr & 0xFFF) << 20;
+ prule->sdram_end = ((protruleaddr >> 12) & 0xFFF) << 20;
+
+ /* Get the configured protection IDs */
+ regoffs = SDR_CTRLGRP_PROTRULEID_ADDRESS;
+ protruleid = readl(SOCFPGA_SDR_ADDRESS + regoffs);
+ prule->lo_prot_id = protruleid & 0xFFF;
+ prule->hi_prot_id = (protruleid >> 12) & 0xFFF;
+
+ /* Get protection data */
+ regoffs = SDR_CTRLGRP_PROTRULEDATA_ADDRESS;
+ protruledata = readl(SOCFPGA_SDR_ADDRESS + regoffs);
+
+ prule->security = protruledata & 0x3;
+ prule->valid = (protruledata >> 2) & 0x1;
+ prule->portmask = (protruledata >> 3) & 0x3FF;
+ prule->result = (protruledata >> 13) & 0x1;
+}
+
+
+void sdram_set_protection_config(uint64_t sdram_start, uint64_t sdram_end)
+{
+ sdram_prot_rule rule;
+ int rules;
+ int regoffs = SDR_CTRLGRP_PROTPORTDEFAULT_ADDRESS;
+
+ /* Start with accepting all SDRAM transaction */
+ writel(0x0, (SOCFPGA_SDR_ADDRESS + regoffs));
+
+ /* Clear all protection rules for warm boot case */
+
+ rule.sdram_start = 0;
+ rule.sdram_end = 0;
+ rule.lo_prot_id = 0;
+ rule.hi_prot_id = 0;
+ rule.portmask = 0;
+ rule.security = 0;
+ rule.result = 0;
+ rule.valid = 0;
+ rule.rule = 0;
+
+ for (rules = 0; rules < 20; rules++) {
+ rule.rule = rules;
+ sdram_set_rule(&rule);
+ }
+
+ /* new rule: accept SDRAM */
+ rule.sdram_start = sdram_start;
+ rule.sdram_end = sdram_end;
+ rule.lo_prot_id = 0x0;
+ rule.hi_prot_id = 0xFFF;
+ rule.portmask = 0x3FF;
+ rule.security = 0x3;
+ rule.result = 0;
+ rule.valid = 1;
+ rule.rule = 0;
+
+ /* set new rule */
+ sdram_set_rule(&rule);
+
+ /* default rule: reject everything */
+ writel(0x3ff, (SOCFPGA_SDR_ADDRESS + regoffs));
+}
+
+void sdram_dump_protection_config(void)
+{
+ sdram_prot_rule rule;
+ int rules;
+ int regoffs = SDR_CTRLGRP_PROTPORTDEFAULT_ADDRESS;
+
+ debug("SDRAM Prot rule, default %x\n",
+ readl(SOCFPGA_SDR_ADDRESS + regoffs));
+
+ for (rules = 0; rules < 20; rules++) {
+ sdram_get_rule(&rule);
+ debug("Rule %d, rules ...\n", rules);
+ debug(" sdram start %llx\n", rule.sdram_start);
+ debug(" sdram end %llx\n", rule.sdram_end);
+ debug(" low prot id %d, hi prot id %d\n",
+ rule.lo_prot_id,
+ rule.hi_prot_id);
+ debug(" portmask %x\n", rule.portmask);
+ debug(" security %d\n", rule.security);
+ debug(" result %d\n", rule.result);
+ debug(" valid %d\n", rule.valid);
+ }
+}
+
+
+
+
/* Function to update the field within variable */
unsigned sdram_write_register_field (unsigned masked_value,
unsigned data, unsigned shift, unsigned mask)
@@ -174,6 +400,23 @@ unsigned sdram_mmr_init_full(unsigned int sdr_phy_reg)
int addrorder;
char *paddrorderinfo = NULL;
+#if defined(CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_CSBITS) && \
+defined(CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS) && \
+defined(CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_BANKBITS) && \
+defined(CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_COLBITS) && \
+defined(CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS)
+
+ int cs = CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_CSBITS;
+ int width = 8;
+ int rows = CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS;
+ int banks = CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_BANKBITS;
+ int cols = CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_COLBITS;
+ unsigned long long workaround_memsize = MEMSIZE_4G;
+
+ writel(CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS,
+ ISWGRP_HANDOFF_ROWBITS);
+#endif
+
DEBUG_MEMORY
/***** CTRLCFG *****/
#if defined(CONFIG_HPS_SDR_CTRLCFG_CTRLCFG_MEMTYPE) || \
@@ -493,8 +736,11 @@ defined(CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_CSBITS)
* Update Preloader to artificially increase the number of rows so
* that the memory thinks it has 4GB of RAM.
*/
+ rows = compute_errata_rows(workaround_memsize, cs, width, rows, banks,
+ cols);
+
reg_value = sdram_write_register_field(reg_value,
- CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS + 1,
+ rows,
SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB,
SDR_CTRLGRP_DRAMADDRW_ROWBITS_MASK);
#endif
@@ -1096,6 +1342,10 @@ defined(CONFIG_HPS_SDR_CTRLCFG_DRAMODT_WRITE)
reg_value = readl(SOCFPGA_SDR_ADDRESS + register_offset);
debug(" Read value without verify 0x%08x\n", (unsigned)reg_value);
+ sdram_set_protection_config(0, sdram_calculate_size());
+
+ sdram_dump_protection_config();
+
DEBUG_MEMORY
return status;
}
@@ -1220,10 +1470,26 @@ unsigned long sdram_calculate_size(void)
* Use ROWBITS from Quartus/QSys to calculate SDRAM size
* since the FB specifies we modify ROWBITs to work around SDRAM
* controller issue.
+ *
+ * If the stored handoff value for rows is 0, it probably means
+ * the preloader is older than UBoot. Use the
+ * #define from the SOCEDS Tools per Crucible review
+ * uboot-socfpga-204. Note that this is not a supported
+ * configuration and is not tested. The customer
+ * should be using preloader and uboot built from the
+ * same tag.
+ */
+ row = readl(ISWGRP_HANDOFF_ROWBITS);
+ if (row == 0)
+ row = CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS;
+ /* If the stored handoff value for rows is greater than
+ * the field width in the sdr.dramaddrw register then
+ * something is very wrong. Revert to using the the #define
+ * value handed off by the SOCEDS tool chain instead of
+ * using a broken value.
*/
- row = (temp & SDR_CTRLGRP_DRAMADDRW_ROWBITS_MASK) >>
- SDR_CTRLGRP_DRAMADDRW_ROWBITS_LSB;
- row -= 1;
+ if (row > 31)
+ row = CONFIG_HPS_SDR_CTRLCFG_DRAMADDRW_ROWBITS;
bank = (temp & SDR_CTRLGRP_DRAMADDRW_BANKBITS_MASK) >>
SDR_CTRLGRP_DRAMADDRW_BANKBITS_LSB;
diff --git a/arch/arm/include/asm/arch-socfpga/sdram.h b/arch/arm/include/asm/arch-socfpga/sdram.h
index d6ea75b8e2..a4bf02b4cb 100644
--- a/arch/arm/include/asm/arch-socfpga/sdram.h
+++ b/arch/arm/include/asm/arch-socfpga/sdram.h
@@ -121,6 +121,16 @@ void sdram_scrub_remain_region_finish(void);
#define SDR_CTRLGRP_FPGAPORTRST_ADDRESS 0x5080
/* Register: sdr.ctrlgrp.fifocfg */
#define SDR_CTRLGRP_FIFOCFG_ADDRESS 0x5088
+/* Register: sdr.ctrlgrp.protportdefault */
+#define SDR_CTRLGRP_PROTPORTDEFAULT_ADDRESS 0x508c
+/* Register: sdr.ctrlgrp.protruleaddr */
+#define SDR_CTRLGRP_PROTRULEADDR_ADDRESS 0x5090
+/* Register: sdr.ctrlgrp.protruleid */
+#define SDR_CTRLGRP_PROTRULEID_ADDRESS 0x5094
+/* Register: sdr.ctrlgrp.protruledata */
+#define SDR_CTRLGRP_PROTRULEDATA_ADDRESS 0x5098
+/* Register: sdr.ctrlgrp.protrulerdwr */
+#define SDR_CTRLGRP_PROTRULERDWR_ADDRESS 0x509c
/* Register: sdr.ctrlgrp.mppriority */
#define SDR_CTRLGRP_MPPRIORITY_ADDRESS 0x50ac
/* Wide Register: sdr.ctrlgrp.mpweight */
diff --git a/arch/arm/include/asm/arch-socfpga/system_manager.h b/arch/arm/include/asm/arch-socfpga/system_manager.h
index d98df81e10..5c771c883c 100644
--- a/arch/arm/include/asm/arch-socfpga/system_manager.h
+++ b/arch/arm/include/asm/arch-socfpga/system_manager.h
@@ -64,6 +64,7 @@ extern unsigned long sys_mgr_init_table[CONFIG_HPS_PINMUX_NUM];
#define ISWGRP_HANDOFF_L3REMAP SYSMGR_ISWGRP_HANDOFF1
#define ISWGRP_HANDOFF_FPGAINTF SYSMGR_ISWGRP_HANDOFF2
#define ISWGRP_HANDOFF_FPGA2SDR SYSMGR_ISWGRP_HANDOFF3
+#define ISWGRP_HANDOFF_ROWBITS SYSMGR_ISWGRP_HANDOFF4
/* pin mux */
#define SYSMGR_PINMUXGRP (SOCFPGA_SYSMGR_ADDRESS + 0x400)
diff --git a/doc/README.SOCFPGA b/doc/README.SOCFPGA
index 8cd925d24c..3c6ef665a3 100755
--- a/doc/README.SOCFPGA
+++ b/doc/README.SOCFPGA
@@ -152,7 +152,7 @@ SYSMGR.ISWGRP.HANDOFF1 - hps l3 remap (L3REGS.REMAP)
SYSMGR.ISWGRP.HANDOFF2 - hps peripheral controller to fpga bridge
(SYSMGR.FPGAINTF.MODULE)
SYSMGR.ISWGRP.HANDOFF3 - fpga to hps sdram bridge (SDR.CTRLGRP.FPGAPORTRST)
-SYSMGR.ISWGRP.HANDOFF4 - unused
+SYSMGR.ISWGRP.HANDOFF4 - row bits handed off by Altera tools
SYSMGR.ISWGRP.HANDOFF5 - unused
SYSMGR.ISWGRP.HANDOFF6 - unused
SYSMGR.ISWGRP.HANDOFF7 - unused