diff options
Diffstat (limited to 'post/cpu/ppc4xx')
-rw-r--r-- | post/cpu/ppc4xx/Makefile | 2 | ||||
-rw-r--r-- | post/cpu/ppc4xx/cache.c | 45 | ||||
-rw-r--r-- | post/cpu/ppc4xx/denali_ecc.c | 267 | ||||
-rw-r--r-- | post/cpu/ppc4xx/ether.c | 89 | ||||
-rw-r--r-- | post/cpu/ppc4xx/uart.c | 15 |
5 files changed, 364 insertions, 54 deletions
diff --git a/post/cpu/ppc4xx/Makefile b/post/cpu/ppc4xx/Makefile index f1034dac24..e3f44b7774 100644 --- a/post/cpu/ppc4xx/Makefile +++ b/post/cpu/ppc4xx/Makefile @@ -24,6 +24,6 @@ LIB = libpostppc4xx.a AOBJS = cache_4xx.o -COBJS = cache.o ether.o fpu.o spr.o uart.o watchdog.o +COBJS = cache.o denali_ecc.o ether.o fpu.o spr.o uart.o watchdog.o include $(TOPDIR)/post/rules.mk diff --git a/post/cpu/ppc4xx/cache.c b/post/cpu/ppc4xx/cache.c index 109ca1fbd1..c86a150692 100644 --- a/post/cpu/ppc4xx/cache.c +++ b/post/cpu/ppc4xx/cache.c @@ -51,8 +51,6 @@ int cache_post_test4 (int tlb, void *p, int size); int cache_post_test5 (int tlb, void *p, int size); int cache_post_test6 (int tlb, void *p, int size); -static int tlb = -1; /* index to the victim TLB entry */ - #ifdef CONFIG_440 static unsigned char testarea[CACHE_POST_SIZE] __attribute__((__aligned__(CACHE_POST_SIZE))); @@ -60,9 +58,10 @@ __attribute__((__aligned__(CACHE_POST_SIZE))); int cache_post_test (int flags) { - void* virt = (void*)CFG_POST_CACHE_ADDR; + void *virt = (void *)CFG_POST_CACHE_ADDR; int ints; int res = 0; + int tlb = -1; /* index to the victim TLB entry */ /* * All 44x variants deal with cache management differently @@ -73,25 +72,23 @@ int cache_post_test (int flags) #ifdef CONFIG_440 int word0, i; - if (tlb < 0) { - /* - * Allocate a new TLB entry, since we are going to modify - * the write-through and caching inhibited storage attributes. - */ - program_tlb((u32)testarea, (u32)virt, - CACHE_POST_SIZE, TLB_WORD2_I_ENABLE); - - /* Find the TLB entry */ - for (i = 0;; i++) { - if (i >= PPC4XX_TLB_SIZE) { - printf ("Failed to program tlb entry\n"); - return -1; - } - word0 = mftlb1(i); - if (TLB_WORD0_EPN_DECODE(word0) == (u32)virt) { - tlb = i; - break; - } + /* + * Allocate a new TLB entry, since we are going to modify + * the write-through and caching inhibited storage attributes. + */ + program_tlb((u32)testarea, (u32)virt, CACHE_POST_SIZE, + TLB_WORD2_I_ENABLE); + + /* Find the TLB entry */ + for (i = 0;; i++) { + if (i >= PPC4XX_TLB_SIZE) { + printf ("Failed to program tlb entry\n"); + return -1; + } + word0 = mftlb1(i); + if (TLB_WORD0_EPN_DECODE(word0) == (u32)virt) { + tlb = i; + break; } } #endif @@ -119,6 +116,10 @@ int cache_post_test (int flags) if (ints) enable_interrupts (); +#ifdef CONFIG_440 + remove_tlb((u32)virt, CACHE_POST_SIZE); +#endif + return res; } diff --git a/post/cpu/ppc4xx/denali_ecc.c b/post/cpu/ppc4xx/denali_ecc.c new file mode 100644 index 0000000000..77234838d9 --- /dev/null +++ b/post/cpu/ppc4xx/denali_ecc.c @@ -0,0 +1,267 @@ +/* + * (C) Copyright 2007 + * Developed for DENX Software Engineering GmbH. + * + * Author: Pavel Kolesnikov <concord@emcraft.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <watchdog.h> + +#if defined(CONFIG_POST) && (defined(CONFIG_440EPX) || defined(CONFIG_440GRX)) + +#include <post.h> + +#if CONFIG_POST & CFG_POST_ECC + +/* + * MEMORY ECC test + * + * This test performs the checks ECC facility of memory. + */ +#include <asm/processor.h> +#include <asm/mmu.h> +#include <asm/io.h> +#include <ppc440.h> + +DECLARE_GLOBAL_DATA_PTR; + +const static unsigned char syndrome_codes[] = { + 0xF4, 0XF1, 0XEC, 0XEA, 0XE9, 0XE6, 0XE5, 0XE3, + 0XDC, 0XDA, 0XD9, 0XD6, 0XD5, 0XD3, 0XCE, 0XCB, + 0xB5, 0XB0, 0XAD, 0XAB, 0XA8, 0XA7, 0XA4, 0XA2, + 0X9D, 0X9B, 0X98, 0X97, 0X94, 0X92, 0X8F, 0X8A, + 0x75, 0x70, 0X6D, 0X6B, 0X68, 0X67, 0X64, 0X62, + 0X5E, 0X5B, 0X58, 0X57, 0X54, 0X52, 0X4F, 0X4A, + 0x34, 0x31, 0X2C, 0X2A, 0X29, 0X26, 0X25, 0X23, + 0X1C, 0X1A, 0X19, 0X16, 0X15, 0X13, 0X0E, 0X0B, + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 +}; + +#define ECC_START_ADDR 0x10 +#define ECC_STOP_ADDR 0x2000 +#define ECC_PATTERN 0x01010101 +#define ECC_PATTERN_CORR 0x11010101 +#define ECC_PATTERN_UNCORR 0xF1010101 + +static int test_ecc_error(void) +{ + unsigned long value; + unsigned long hdata, ldata, haddr, laddr; + unsigned int bit; + + int ret = 0; + + mfsdram(DDR0_23, value); + + for (bit = 0; bit < sizeof(syndrome_codes); bit++) + if (syndrome_codes[bit] == ((value >> 16) & 0xff)) + break; + + mfsdram(DDR0_00, value); + + if (value & DDR0_00_INT_STATUS_BIT0) { + debug("Bit0. A single access outside the defined PHYSICAL" + " memory space detected\n"); + mfsdram(DDR0_32, laddr); + mfsdram(DDR0_33, haddr); + debug(" addr = 0x%08x%08x\n", haddr, laddr); + ret = 1; + } + if (value & DDR0_00_INT_STATUS_BIT1) { + debug("Bit1. Multiple accesses outside the defined PHYSICAL" + " memory space detected\n"); + ret = 2; + } + if (value & DDR0_00_INT_STATUS_BIT2) { + debug("Bit2. Single correctable ECC event detected\n"); + mfsdram(DDR0_38, laddr); + mfsdram(DDR0_39, haddr); + mfsdram(DDR0_40, ldata); + mfsdram(DDR0_41, hdata); + debug(" 0x%08x - 0x%08x%08x, bit - %d\n", + laddr, hdata, ldata, bit); + ret = 3; + } + if (value & DDR0_00_INT_STATUS_BIT3) { + debug("Bit3. Multiple correctable ECC events detected\n"); + mfsdram(DDR0_38, laddr); + mfsdram(DDR0_39, haddr); + mfsdram(DDR0_40, ldata); + mfsdram(DDR0_41, hdata); + debug(" 0x%08x - 0x%08x%08x, bit - %d\n", + laddr, hdata, ldata, bit); + ret = 4; + } + if (value & DDR0_00_INT_STATUS_BIT4) { + debug("Bit4. Single uncorrectable ECC event detected\n"); + mfsdram(DDR0_34, laddr); + mfsdram(DDR0_35, haddr); + mfsdram(DDR0_36, ldata); + mfsdram(DDR0_37, hdata); + debug(" 0x%08x - 0x%08x%08x, bit - %d\n", + laddr, hdata, ldata, bit); + ret = 5; + } + if (value & DDR0_00_INT_STATUS_BIT5) { + debug("Bit5. Multiple uncorrectable ECC events detected\n"); + mfsdram(DDR0_34, laddr); + mfsdram(DDR0_35, haddr); + mfsdram(DDR0_36, ldata); + mfsdram(DDR0_37, hdata); + debug(" 0x%08x - 0x%08x%08x, bit - %d\n", + laddr, hdata, ldata, bit); + ret = 6; + } + if (value & DDR0_00_INT_STATUS_BIT6) { + debug("Bit6. DRAM initialization complete\n"); + ret = 7; + } + + /* error status cleared */ + mfsdram(DDR0_00, value); + mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL); + + return ret; +} + +static int test_ecc(unsigned long ecc_addr) +{ + unsigned long value; + volatile unsigned *const ecc_mem = (volatile unsigned *) ecc_addr; + int pret; + int ret = 0; + + sync(); + eieio(); + WATCHDOG_RESET(); + + debug("Entering test_ecc(0x%08lX)\n", ecc_addr); + out_be32(ecc_mem, ECC_PATTERN); + out_be32(ecc_mem + 1, ECC_PATTERN); + in_be32(ecc_mem); + pret = test_ecc_error(); + if (pret != 0) { + debug("pret: expected 0, got %d\n", pret); + ret = 1; + } + /* test for correctable error */ + /* disconnect from ecc storage */ + mfsdram(DDR0_22, value); + mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) + | DDR0_22_CTRL_RAW_ECC_DISABLE); + + /* creating (correctable) single-bit error */ + out_be32(ecc_mem, ECC_PATTERN_CORR); + + /* enable ecc */ + mfsdram(DDR0_22, value); + mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) + | DDR0_22_CTRL_RAW_ECC_ENABLE); + sync(); + eieio(); + + in_be32(ecc_mem); + pret = test_ecc_error(); + /* if read data ok, 1 correctable error must be fixed */ + if (pret != 3) { + debug("pret: expected 3, got %d\n", pret); + ret = 1; + } + /* test for uncorrectable error */ + /* disconnect from ecc storage */ + mfsdram(DDR0_22, value); + mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) + | DDR0_22_CTRL_RAW_NO_ECC_RAM); + + /* creating (uncorrectable) multiple-bit error */ + out_be32(ecc_mem, ECC_PATTERN_UNCORR); + + /* enable ecc */ + mfsdram(DDR0_22, value); + mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) + | DDR0_22_CTRL_RAW_ECC_ENABLE); + sync(); + eieio(); + + in_be32(ecc_mem); + pret = test_ecc_error(); + /* info about uncorrectable error must appear */ + if (pret != 5) { + debug("pret: expected 5, got %d\n", pret); + ret = 1; + } + /* remove error from SDRAM */ + out_be32(ecc_mem, ECC_PATTERN); + /* clear error caused by read-modify-write */ + mfsdram(DDR0_00, value); + mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL); + + sync(); + eieio(); + return ret; +} + +int ecc_post_test (int flags) +{ + int ret = 0; + unsigned long value; + unsigned long iaddr; + + sync(); + eieio(); + + mfsdram(DDR0_22, value); + if (0x3 != DDR0_22_CTRL_RAW_DECODE(value)) { + debug("SDRAM ECC not enabled, skipping ECC POST.\n"); + return 0; + } + + /* mask all int */ + mfsdram(DDR0_01, value); + mtsdram(DDR0_01, (value & ~DDR0_01_INT_MASK_MASK) + | DDR0_01_INT_MASK_ALL_OFF); + + /* clear error status */ + mfsdram(DDR0_00, value); + mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL); + + for (iaddr = ECC_START_ADDR; iaddr <= ECC_STOP_ADDR; iaddr += iaddr) { + ret = test_ecc(iaddr); + if (ret) + break; + } + /* + * Clear possible errors resulting from ECC testing. + * If not done, then we could get an interrupt later on when + * exceptions are enabled. + */ + set_mcsr(get_mcsr()); + return ret; + +} +#endif /* CONFIG_POST & CFG_POST_ECC */ +#endif /* defined(CONFIG_POST) && ... */ diff --git a/post/cpu/ppc4xx/ether.c b/post/cpu/ppc4xx/ether.c index ab23ca5a3d..4ac7491343 100644 --- a/post/cpu/ppc4xx/ether.c +++ b/post/cpu/ppc4xx/ether.c @@ -52,6 +52,28 @@ DECLARE_GLOBAL_DATA_PTR; +/* + * Get count of EMAC devices (doesn't have to be the max. possible number + * supported by the cpu) + * + * CONFIG_BOARD_EMAC_COUNT added so now a "dynamic" way to configure the + * EMAC count is possible. As it is needed for the Kilauea/Haleakala + * 405EX/405EXr eval board, using the same binary. + */ +#if defined(CONFIG_BOARD_EMAC_COUNT) +#define LAST_EMAC_NUM board_emac_count() +#else /* CONFIG_BOARD_EMAC_COUNT */ +#if defined(CONFIG_HAS_ETH3) +#define LAST_EMAC_NUM 4 +#elif defined(CONFIG_HAS_ETH2) +#define LAST_EMAC_NUM 3 +#elif defined(CONFIG_HAS_ETH1) +#define LAST_EMAC_NUM 2 +#else +#define LAST_EMAC_NUM 1 +#endif +#endif /* CONFIG_BOARD_EMAC_COUNT */ + #if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX) #define SDR0_MFR_ETH_CLK_SEL_V(n) ((0x01<<27) / (n+1)) #endif @@ -65,6 +87,8 @@ static volatile mal_desc_t rx __cacheline_aligned; static char *tx_buf; static char *rx_buf; +int board_emac_count(void); + static void ether_post_init (int devnum, int hw_addr) { int i; @@ -93,11 +117,11 @@ static void ether_post_init (int devnum, int hw_addr) sync (); #endif /* reset emac */ - out32 (EMAC_M0 + hw_addr, EMAC_M0_SRST); + out_be32 ((void*)(EMAC_M0 + hw_addr), EMAC_M0_SRST); sync (); for (i = 0;; i++) { - if (!(in32 (EMAC_M0 + hw_addr) & EMAC_M0_SRST)) + if (!(in_be32 ((void*)(EMAC_M0 + hw_addr)) & EMAC_M0_SRST)) break; if (i >= 1000) { printf ("Timeout resetting EMAC\n"); @@ -120,7 +144,7 @@ static void ether_post_init (int devnum, int hw_addr) else mode_reg |= EMAC_M1_OBCI_GT100; - out32 (EMAC_M1 + hw_addr, mode_reg); + out_be32 ((void*)(EMAC_M1 + hw_addr), mode_reg); #endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */ @@ -145,6 +169,8 @@ static void ether_post_init (int devnum, int hw_addr) rx.ctrl = MAL_TX_CTRL_WRAP | MAL_RX_CTRL_EMPTY; rx.data_len = 0; rx.data_ptr = (char*)L1_CACHE_ALIGN((u32)rx_buf); + flush_dcache_range((u32)&rx, (u32)&rx + sizeof(mal_desc_t)); + flush_dcache_range((u32)&tx, (u32)&tx + sizeof(mal_desc_t)); switch (devnum) { case 1: @@ -186,40 +212,40 @@ static void ether_post_init (int devnum, int hw_addr) /* set internal loopback mode */ #ifdef CFG_POST_ETHER_EXT_LOOPBACK - out32 (EMAC_M1 + hw_addr, EMAC_M1_FDE | 0 | - EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K | - EMAC_M1_MF_100MBPS | EMAC_M1_IST | - in32 (EMAC_M1)); + out_be32 ((void*)(EMAC_M1 + hw_addr), EMAC_M1_FDE | 0 | + EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K | + EMAC_M1_MF_100MBPS | EMAC_M1_IST | + in_be32 ((void*)(EMAC_M1 + hw_addr))); #else - out32 (EMAC_M1 + hw_addr, EMAC_M1_FDE | EMAC_M1_ILE | - EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K | - EMAC_M1_MF_100MBPS | EMAC_M1_IST | - in32 (EMAC_M1)); + out_be32 ((void*)(EMAC_M1 + hw_addr), EMAC_M1_FDE | EMAC_M1_ILE | + EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K | + EMAC_M1_MF_100MBPS | EMAC_M1_IST | + in_be32 ((void*)(EMAC_M1 + hw_addr))); #endif /* set transmit enable & receive enable */ - out32 (EMAC_M0 + hw_addr, EMAC_M0_TXE | EMAC_M0_RXE); + out_be32 ((void*)(EMAC_M0 + hw_addr), EMAC_M0_TXE | EMAC_M0_RXE); /* enable broadcast address */ - out32 (EMAC_RXM + hw_addr, EMAC_RMR_BAE); + out_be32 ((void*)(EMAC_RXM + hw_addr), EMAC_RMR_BAE); /* set transmit request threshold register */ - out32 (EMAC_TRTR + hw_addr, 0x18000000); /* 256 byte threshold */ + out_be32 ((void*)(EMAC_TRTR + hw_addr), 0x18000000); /* 256 byte threshold */ /* set receive low/high water mark register */ #if defined(CONFIG_440) /* 440s has a 64 byte burst length */ - out32 (EMAC_RX_HI_LO_WMARK + hw_addr, 0x80009000); + out_be32 ((void*)(EMAC_RX_HI_LO_WMARK + hw_addr), 0x80009000); #else /* 405s have a 16 byte burst length */ - out32 (EMAC_RX_HI_LO_WMARK + hw_addr, 0x0f002000); + out_be32 ((void*)(EMAC_RX_HI_LO_WMARK + hw_addr), 0x0f002000); #endif /* defined(CONFIG_440) */ - out32 (EMAC_TXM1 + hw_addr, 0xf8640000); + out_be32 ((void*)(EMAC_TXM1 + hw_addr), 0xf8640000); /* Set fifo limit entry in tx mode 0 */ - out32 (EMAC_TXM0 + hw_addr, 0x00000003); + out_be32 ((void*)(EMAC_TXM0 + hw_addr), 0x00000003); /* Frame gap set */ - out32 (EMAC_I_FRAME_GAP_REG + hw_addr, 0x00000008); + out_be32 ((void*)(EMAC_I_FRAME_GAP_REG + hw_addr), 0x00000008); sync (); } @@ -246,7 +272,7 @@ static void ether_post_halt (int devnum, int hw_addr) udelay (1000); } /* emac reset */ - out32 (EMAC_M0 + hw_addr, EMAC_M0_SRST); + out_be32 ((void*)(EMAC_M0 + hw_addr), EMAC_M0_SRST); #if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX) /* remove clocks for EMAC internal loopback */ @@ -266,14 +292,17 @@ static void ether_post_send (int devnum, int hw_addr, void *packet, int length) return; } udelay (1000); + invalidate_dcache_range((u32)&tx, (u32)&tx + sizeof(mal_desc_t)); } tx.ctrl = MAL_TX_CTRL_READY | MAL_TX_CTRL_WRAP | MAL_TX_CTRL_LAST | EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP; tx.data_len = length; memcpy (tx.data_ptr, packet, length); + flush_dcache_range((u32)&tx, (u32)&tx + sizeof(mal_desc_t)); + flush_dcache_range((u32)tx.data_ptr, (u32)tx.data_ptr + length); sync (); - out32 (EMAC_TXM0 + hw_addr, in32 (EMAC_TXM0 + hw_addr) | EMAC_TXM0_GNP0); + out_be32 ((void*)(EMAC_TXM0 + hw_addr), in_be32 ((void*)(EMAC_TXM0 + hw_addr)) | EMAC_TXM0_GNP0); sync (); } @@ -288,13 +317,17 @@ static int ether_post_recv (int devnum, int hw_addr, void *packet, int max_lengt return 0; } udelay (1000); + invalidate_dcache_range((u32)&rx, (u32)&rx + sizeof(mal_desc_t)); } length = rx.data_len - 4; - if (length <= max_length) + if (length <= max_length) { + invalidate_dcache_range((u32)rx.data_ptr, (u32)rx.data_ptr + length); memcpy(packet, rx.data_ptr, length); + } sync (); rx.ctrl |= MAL_RX_CTRL_EMPTY; + flush_dcache_range((u32)&rx, (u32)&rx + sizeof(mal_desc_t)); sync (); return length; @@ -372,6 +405,7 @@ Done: int ether_post_test (int flags) { int res = 0; + int i; /* Allocate tx & rx packet buffers */ tx_buf = malloc (PKTSIZE_ALIGN + CFG_CACHELINE_SIZE); @@ -383,13 +417,10 @@ int ether_post_test (int flags) goto out_free; } - /* EMAC0 */ - if (test_ctlr (0, 0)) - res = -1; - - /* EMAC1 */ - if (test_ctlr (1, 0x100)) - res = -1; + for (i = 0; i < LAST_EMAC_NUM; i++) { + if (test_ctlr (i, i*0x100)) + res = -1; + } out_free: free (tx_buf); diff --git a/post/cpu/ppc4xx/uart.c b/post/cpu/ppc4xx/uart.c index 7c3ed402c1..f47b48e9dc 100644 --- a/post/cpu/ppc4xx/uart.c +++ b/post/cpu/ppc4xx/uart.c @@ -101,6 +101,17 @@ #define UCR0_UDIV_POS 0 #define UCR1_UDIV_POS 8 #define UDIV_MAX 127 +#elif defined(CONFIG_405EX) +#define UART0_BASE 0xef600200 +#define UART1_BASE 0xef600300 +#define CR0_MASK 0x000000ff +#define CR0_EXTCLK_ENA 0x00800000 +#define CR0_UDIV_POS 0 +#define UDIV_SUBTRACT 0 +#define UART0_SDR sdr_uart0 +#define UART1_SDR sdr_uart1 +#define MFREG(a, d) mfsdr(a, d) +#define MTREG(a, d) mtsdr(a, d) #else /* CONFIG_405GP || CONFIG_405CR */ #define UART0_BASE 0xef600300 #define UART1_BASE 0xef600400 @@ -137,7 +148,7 @@ DECLARE_GLOBAL_DATA_PTR; -#if defined(CONFIG_440) +#if defined(CONFIG_440) || defined(CONFIG_405EX) #if !defined(CFG_EXT_SERIAL_CLOCK) static void serial_divs (int baudrate, unsigned long *pudiv, unsigned short *pbdiv) @@ -183,7 +194,7 @@ static void serial_divs (int baudrate, unsigned long *pudiv, static int uart_post_init (unsigned long dev_base) { - unsigned long reg; + unsigned long reg = 0; unsigned long udiv; unsigned short bdiv; volatile char val; |