| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright 2015 MediaTek Inc. |
| * |
| * 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; version 2 of the License. |
| * |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <arch/io.h> |
| #include <assert.h> |
| #include <console/console.h> |
| #include <soc/addressmap.h> |
| #include <soc/dramc_register.h> |
| #include <soc/dramc_pi_api.h> |
| #include <soc/emi.h> |
| #include <soc/pll.h> |
| |
| enum { |
| /* test patterns */ |
| PATTERN0 = 0x00000000, |
| PATTERN1 = 0x5A5A5A5A, |
| PATTERN2 = 0xA5A5A5A5, |
| PATTERN3 = 0xA5A5A500, |
| PATTERN4 = 0xA500A500, |
| PATTERN5 = 0xA5000000, |
| PATTERN6 = 0xFFFF0000, |
| PATTERN7 = 0x0000FFFF, |
| PATTERN8 = 0x00000012, |
| PATTERN9 = 0x00000034, |
| PATTERNA = 0x00000056, |
| PATTERNB = 0x00000078, |
| PATTERNC = 0x00001234, |
| PATTERND = 0x00005678, |
| PATTERNE = 0x12345678, |
| PATTERNF = 0xFFFFFFFF |
| }; |
| |
| static int complex_mem_test(unsigned int start, unsigned int len) |
| { |
| unsigned char *mem8_base = (unsigned char *)(uintptr_t)start; |
| unsigned short *mem16_base = (unsigned short *)(uintptr_t)start; |
| unsigned int *mem32_base = (unsigned int *)(uintptr_t)start; |
| unsigned int *mem_base = (unsigned int *)(uintptr_t)start; |
| unsigned char pattern8; |
| unsigned short pattern16; |
| unsigned int i, j, size, pattern32; |
| unsigned int value; |
| |
| size = len >> 2; |
| |
| /* verify the tied bits (tied high) */ |
| for (i = 0; i < size; i++) { |
| mem32_base[i] = PATTERN0; |
| } |
| |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERN0) { |
| return -1; |
| } else { |
| mem32_base[i] = PATTERNF; |
| } |
| } |
| |
| /* verify the tied bits (tied low) */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERNF) { |
| return -2; |
| } else |
| mem32_base[i] = PATTERN0; |
| } |
| |
| /* verify pattern 1 (0x00~0xff) */ |
| pattern8 = PATTERN0; |
| for (i = 0; i < len; i++) |
| mem8_base[i] = pattern8++; |
| pattern8 = PATTERN0; |
| for (i = 0; i < len; i++) { |
| if (mem8_base[i] != pattern8++) { |
| return -3; |
| } |
| } |
| |
| /* verify pattern 2 (0x00~0xff) */ |
| pattern8 = PATTERN0; |
| for (i = j = 0; i < len; i += 2, j++) { |
| if (mem8_base[i] == pattern8) |
| mem16_base[j] = pattern8; |
| if (mem16_base[j] != pattern8) { |
| return -4; |
| } |
| pattern8 += 2; |
| } |
| |
| /* verify pattern 3 (0x00~0xffff) */ |
| pattern16 = PATTERN0; |
| for (i = 0; i < (len >> 1); i++) |
| mem16_base[i] = pattern16++; |
| pattern16 = PATTERN0; |
| for (i = 0; i < (len >> 1); i++) { |
| if (mem16_base[i] != pattern16++) { |
| return -5; |
| } |
| } |
| |
| /* verify pattern 4 (0x00~0xffffffff) */ |
| pattern32 = PATTERN0; |
| for (i = 0; i < (len >> 2); i++) |
| mem32_base[i] = pattern32++; |
| pattern32 = PATTERN0; |
| for (i = 0; i < (len >> 2); i++) { |
| if (mem32_base[i] != pattern32++) { |
| return -6; |
| } |
| } |
| |
| /* pattern 5: filling memory range with 0x12345678 */ |
| for (i = 0; i < size; i++) |
| mem32_base[i] = PATTERNE; |
| |
| /* read check then fill memory with a5a5a5a5 pattern */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERNE) { |
| return -7; |
| } else { |
| mem32_base[i] = PATTERN2; |
| } |
| } |
| |
| /* read check then fill memory with 00 byte pattern at offset 0h */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERN2) { |
| return -8; |
| } else { |
| mem8_base[i * 4] = PATTERN0; |
| } |
| } |
| |
| /* read check then fill memory with 00 byte pattern at offset 2h */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERN3) { |
| return -9; |
| } else { |
| mem8_base[i * 4 + 2] = PATTERN0; |
| } |
| } |
| |
| /* read check then fill memory with 00 byte pattern at offset 1h */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERN4) { |
| return -10; |
| } else { |
| mem8_base[i * 4 + 1] = PATTERN0; |
| } |
| } |
| |
| /* read check then fill memory with 00 byte pattern at offset 3h */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERN5) { |
| return -11; |
| } else { |
| mem8_base[i * 4 + 3] = PATTERN0; |
| } |
| } |
| |
| /* read check then fill memory with ffff word pattern at offset 1h */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERN0) { |
| return -12; |
| } else { |
| mem16_base[i * 2 + 1] = PATTERN7; |
| } |
| } |
| |
| /* read check then fill memory with ffff word pattern at offset 0h */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERN6) { |
| return -13; |
| } else { |
| mem16_base[i * 2] = PATTERN7; |
| } |
| } |
| |
| /* read check */ |
| for (i = 0; i < size; i++) { |
| if (mem32_base[i] != PATTERNF) { |
| return -14; |
| } |
| } |
| |
| /* stage 1 => write 0 */ |
| for (i = 0; i < size; i++) { |
| mem_base[i] = PATTERN1; |
| } |
| |
| /* stage 2 => read 0, write 0xf */ |
| for (i = 0; i < size; i++) { |
| value = mem_base[i]; |
| |
| if (value != PATTERN1) { |
| return -15; |
| } |
| mem_base[i] = PATTERN2; |
| } |
| |
| /* stage 3 => read 0xf, write 0 */ |
| for (i = 0; i < size; i++) { |
| value = mem_base[i]; |
| if (value != PATTERN2) { |
| return -16; |
| } |
| mem_base[i] = PATTERN1; |
| } |
| |
| /* stage 4 => read 0, write 0xf */ |
| for (i = 0; i < size; i++) { |
| value = mem_base[i]; |
| if (value != PATTERN1) { |
| return -17; |
| } |
| mem_base[i] = PATTERN2; |
| } |
| |
| /* stage 5 => read 0xf, write 0 */ |
| for (i = 0; i < size; i++) { |
| value = mem_base[i]; |
| if (value != PATTERN2) { |
| return -18; |
| } |
| mem_base[i] = PATTERN1; |
| } |
| |
| /* stage 6 => read 0 */ |
| for (i = 0; i < size; i++) { |
| value = mem_base[i]; |
| if (value != PATTERN1) { |
| return -19; |
| } |
| } |
| |
| /* 1/2/4-byte combination test */ |
| i = (unsigned int)(uintptr_t)mem_base; |
| |
| while (i < (unsigned int)(uintptr_t)mem_base + (size << 2)) { |
| *((unsigned char *)(uintptr_t)i) = PATTERNB; |
| i += 1; |
| *((unsigned char *)(uintptr_t)i) = PATTERNA; |
| i += 1; |
| *((unsigned short *)(uintptr_t)i) = PATTERNC; |
| i += 2; |
| *((unsigned int *)(uintptr_t)i) = PATTERNE; |
| i += 4; |
| *((unsigned short *)(uintptr_t)i) = PATTERND; |
| i += 2; |
| *((unsigned char *)(uintptr_t)i) = PATTERN9; |
| i += 1; |
| *((unsigned char *)(uintptr_t)i) = PATTERN8; |
| i += 1; |
| *((unsigned int *)(uintptr_t)i) = PATTERNE; |
| i += 4; |
| *((unsigned char *)(uintptr_t)i) = PATTERNB; |
| i += 1; |
| *((unsigned char *)(uintptr_t)i) = PATTERNA; |
| i += 1; |
| *((unsigned short *)(uintptr_t)i) = PATTERNC; |
| i += 2; |
| *((unsigned int *)(uintptr_t)i) = PATTERNE; |
| i += 4; |
| *((unsigned short *)(uintptr_t)i) = PATTERND; |
| i += 2; |
| *((unsigned char *)(uintptr_t)i) = PATTERN9; |
| i += 1; |
| *((unsigned char *)(uintptr_t)i) = PATTERN8; |
| i += 1; |
| *((unsigned int *)(uintptr_t)i) = PATTERNE; |
| i += 4; |
| } |
| |
| for (i = 0; i < size; i++) { |
| value = mem_base[i]; |
| if (value != PATTERNE) { |
| return -20; |
| } |
| } |
| |
| /* verify pattern 1 (0x00~0xff) */ |
| pattern8 = PATTERN0; |
| mem8_base[0] = pattern8; |
| for (i = 0; i < size * 4; i++) { |
| unsigned char waddr8, raddr8; |
| |
| waddr8 = i + 1; |
| raddr8 = i; |
| if (i < size * 4 - 1) |
| mem8_base[waddr8] = pattern8 + 1; |
| if (mem8_base[raddr8] != pattern8) { |
| return -21; |
| } |
| pattern8++; |
| } |
| |
| /* verify pattern 2 (0x00~0xffff) */ |
| pattern16 = PATTERN0; |
| mem16_base[0] = pattern16; |
| for (i = 0; i < size * 2; i++) { |
| if (i < size * 2 - 1) |
| mem16_base[i + 1] = pattern16 + 1; |
| if (mem16_base[i] != pattern16) { |
| return -22; |
| } |
| pattern16++; |
| } |
| |
| /* verify pattern 3 (0x00~0xffffffff) */ |
| pattern32 = PATTERN0; |
| mem32_base[0] = pattern32; |
| for (i = 0; i < size; i++) { |
| if (i < size - 1) |
| mem32_base[i + 1] = pattern32 + 1; |
| if (mem32_base[i] != pattern32) { |
| return -23; |
| } |
| pattern32++; |
| } |
| |
| return 0; |
| } |
| |
| void mt_mem_init(const struct mt8173_sdram_params *sdram_params) |
| { |
| int i = 0; |
| |
| /* init mempll */ |
| mem_pll_init(sdram_params); |
| |
| /* memory calibration */ |
| mt_set_emi(sdram_params); |
| |
| if (IS_ENABLED(CONFIG_MEMORY_TEST)) { |
| /* do memory test: |
| * set memory scan range 0x2000 |
| * larger test length, longer system boot up time |
| */ |
| i = complex_mem_test(DDR_BASE, 0x2000); |
| |
| printk(BIOS_DEBUG, "[MEM] complex R/W mem test %s : %d\n", |
| (i == 0) ? "pass" : "fail", i); |
| |
| ASSERT(i == 0); |
| } |
| } |