diff options
Diffstat (limited to 'arch/x86/cpu/quark/hte.c')
-rw-r--r-- | arch/x86/cpu/quark/hte.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/arch/x86/cpu/quark/hte.c b/arch/x86/cpu/quark/hte.c new file mode 100644 index 0000000000..372815d8c1 --- /dev/null +++ b/arch/x86/cpu/quark/hte.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +#include <common.h> +#include <asm/arch/mrc.h> +#include <asm/arch/msg_port.h> +#include "mrc_util.h" +#include "hte.h" + +/** + * Enable HTE to detect all possible errors for the given training parameters + * (per-bit or full byte lane). + */ +static void hte_enable_all_errors(void) +{ + msg_port_write(HTE, 0x000200A2, 0xFFFFFFFF); + msg_port_write(HTE, 0x000200A3, 0x000000FF); + msg_port_write(HTE, 0x000200A4, 0x00000000); +} + +/** + * Go and read the HTE register in order to find any error + * + * @return: The errors detected in the HTE status register + */ +static u32 hte_check_errors(void) +{ + return msg_port_read(HTE, 0x000200A7); +} + +/** + * Wait until HTE finishes + */ +static void hte_wait_for_complete(void) +{ + u32 tmp; + + ENTERFN(); + + do {} while ((msg_port_read(HTE, 0x00020012) & BIT30) != 0); + + tmp = msg_port_read(HTE, 0x00020011); + tmp |= BIT9; + tmp &= ~(BIT12 | BIT13); + msg_port_write(HTE, 0x00020011, tmp); + + LEAVEFN(); +} + +/** + * Clear registers related with errors in the HTE + */ +static void hte_clear_error_regs(void) +{ + u32 tmp; + + /* + * Clear all HTE errors and enable error checking + * for burst and chunk. + */ + tmp = msg_port_read(HTE, 0x000200A1); + tmp |= BIT8; + msg_port_write(HTE, 0x000200A1, tmp); +} + +/** + * Execute a basic single-cache-line memory write/read/verify test using simple + * constant pattern, different for READ_TRAIN and WRITE_TRAIN modes. + * + * See hte_basic_write_read() which is the external visible wrapper. + * + * @mrc_params: host structure for all MRC global data + * @addr: memory adress being tested (must hit specific channel/rank) + * @first_run: if set then the HTE registers are configured, otherwise it is + * assumed configuration is done and we just re-run the test + * @mode: READ_TRAIN or WRITE_TRAIN (the difference is in the pattern) + * + * @return: byte lane failure on each bit (for Quark only bit0 and bit1) + */ +static u16 hte_basic_data_cmp(struct mrc_params *mrc_params, u32 addr, + u8 first_run, u8 mode) +{ + u32 pattern; + u32 offset; + + if (first_run) { + msg_port_write(HTE, 0x00020020, 0x01B10021); + msg_port_write(HTE, 0x00020021, 0x06000000); + msg_port_write(HTE, 0x00020022, addr >> 6); + msg_port_write(HTE, 0x00020062, 0x00800015); + msg_port_write(HTE, 0x00020063, 0xAAAAAAAA); + msg_port_write(HTE, 0x00020064, 0xCCCCCCCC); + msg_port_write(HTE, 0x00020065, 0xF0F0F0F0); + msg_port_write(HTE, 0x00020061, 0x00030008); + + if (mode == WRITE_TRAIN) + pattern = 0xC33C0000; + else /* READ_TRAIN */ + pattern = 0xAA5555AA; + + for (offset = 0x80; offset <= 0x8F; offset++) + msg_port_write(HTE, offset, pattern); + } + + msg_port_write(HTE, 0x000200A1, 0xFFFF1000); + msg_port_write(HTE, 0x00020011, 0x00011000); + msg_port_write(HTE, 0x00020011, 0x00011100); + + hte_wait_for_complete(); + + /* + * Return bits 15:8 of HTE_CH0_ERR_XSTAT to check for + * any bytelane errors. + */ + return (hte_check_errors() >> 8) & 0xFF; +} + +/** + * Examine a single-cache-line memory with write/read/verify test using multiple + * data patterns (victim-aggressor algorithm). + * + * See hte_write_stress_bit_lanes() which is the external visible wrapper. + * + * @mrc_params: host structure for all MRC global data + * @addr: memory adress being tested (must hit specific channel/rank) + * @loop_cnt: number of test iterations + * @seed_victim: victim data pattern seed + * @seed_aggressor: aggressor data pattern seed + * @victim_bit: should be 0 as auto-rotate feature is in use + * @first_run: if set then the HTE registers are configured, otherwise it is + * assumed configuration is done and we just re-run the test + * + * @return: byte lane failure on each bit (for Quark only bit0 and bit1) + */ +static u16 hte_rw_data_cmp(struct mrc_params *mrc_params, u32 addr, + u8 loop_cnt, u32 seed_victim, u32 seed_aggressor, + u8 victim_bit, u8 first_run) +{ + u32 offset; + u32 tmp; + + if (first_run) { + msg_port_write(HTE, 0x00020020, 0x00910024); + msg_port_write(HTE, 0x00020023, 0x00810024); + msg_port_write(HTE, 0x00020021, 0x06070000); + msg_port_write(HTE, 0x00020024, 0x06070000); + msg_port_write(HTE, 0x00020022, addr >> 6); + msg_port_write(HTE, 0x00020025, addr >> 6); + msg_port_write(HTE, 0x00020062, 0x0000002A); + msg_port_write(HTE, 0x00020063, seed_victim); + msg_port_write(HTE, 0x00020064, seed_aggressor); + msg_port_write(HTE, 0x00020065, seed_victim); + + /* + * Write the pattern buffers to select the victim bit + * + * Start with bit0 + */ + for (offset = 0x80; offset <= 0x8F; offset++) { + if ((offset % 8) == victim_bit) + msg_port_write(HTE, offset, 0x55555555); + else + msg_port_write(HTE, offset, 0xCCCCCCCC); + } + + msg_port_write(HTE, 0x00020061, 0x00000000); + msg_port_write(HTE, 0x00020066, 0x03440000); + msg_port_write(HTE, 0x000200A1, 0xFFFF1000); + } + + tmp = 0x10001000 | (loop_cnt << 16); + msg_port_write(HTE, 0x00020011, tmp); + msg_port_write(HTE, 0x00020011, tmp | BIT8); + + hte_wait_for_complete(); + + /* + * Return bits 15:8 of HTE_CH0_ERR_XSTAT to check for + * any bytelane errors. + */ + return (hte_check_errors() >> 8) & 0xFF; +} + +/** + * Use HW HTE engine to initialize or test all memory attached to a given DUNIT. + * If flag is MRC_MEM_INIT, this routine writes 0s to all memory locations to + * initialize ECC. If flag is MRC_MEM_TEST, this routine will send an 5AA55AA5 + * pattern to all memory locations on the RankMask and then read it back. + * Then it sends an A55AA55A pattern to all memory locations on the RankMask + * and reads it back. + * + * @mrc_params: host structure for all MRC global data + * @flag: MRC_MEM_INIT or MRC_MEM_TEST + * + * @return: errors register showing HTE failures. Also prints out which rank + * failed the HTE test if failure occurs. For rank detection to work, + * the address map must be left in its default state. If MRC changes + * the address map, this function must be modified to change it back + * to default at the beginning, then restore it at the end. + */ +u32 hte_mem_init(struct mrc_params *mrc_params, u8 flag) +{ + u32 offset; + int test_num; + int i; + + /* + * Clear out the error registers at the start of each memory + * init or memory test run. + */ + hte_clear_error_regs(); + + msg_port_write(HTE, 0x00020062, 0x00000015); + + for (offset = 0x80; offset <= 0x8F; offset++) + msg_port_write(HTE, offset, ((offset & 1) ? 0xA55A : 0x5AA5)); + + msg_port_write(HTE, 0x00020021, 0x00000000); + msg_port_write(HTE, 0x00020022, (mrc_params->mem_size >> 6) - 1); + msg_port_write(HTE, 0x00020063, 0xAAAAAAAA); + msg_port_write(HTE, 0x00020064, 0xCCCCCCCC); + msg_port_write(HTE, 0x00020065, 0xF0F0F0F0); + msg_port_write(HTE, 0x00020066, 0x03000000); + + switch (flag) { + case MRC_MEM_INIT: + /* + * Only 1 write pass through memory is needed + * to initialize ECC + */ + test_num = 1; + break; + case MRC_MEM_TEST: + /* Write/read then write/read with inverted pattern */ + test_num = 4; + break; + default: + DPF(D_INFO, "Unknown parameter for flag: %d\n", flag); + return 0xFFFFFFFF; + } + + DPF(D_INFO, "hte_mem_init"); + + for (i = 0; i < test_num; i++) { + DPF(D_INFO, "."); + + if (i == 0) { + msg_port_write(HTE, 0x00020061, 0x00000000); + msg_port_write(HTE, 0x00020020, 0x00110010); + } else if (i == 1) { + msg_port_write(HTE, 0x00020061, 0x00000000); + msg_port_write(HTE, 0x00020020, 0x00010010); + } else if (i == 2) { + msg_port_write(HTE, 0x00020061, 0x00010100); + msg_port_write(HTE, 0x00020020, 0x00110010); + } else { + msg_port_write(HTE, 0x00020061, 0x00010100); + msg_port_write(HTE, 0x00020020, 0x00010010); + } + + msg_port_write(HTE, 0x00020011, 0x00111000); + msg_port_write(HTE, 0x00020011, 0x00111100); + + hte_wait_for_complete(); + + /* If this is a READ pass, check for errors at the end */ + if ((i % 2) == 1) { + /* Return immediately if error */ + if (hte_check_errors()) + break; + } + } + + DPF(D_INFO, "done\n"); + + return hte_check_errors(); +} + +/** + * Execute a basic single-cache-line memory write/read/verify test using simple + * constant pattern, different for READ_TRAIN and WRITE_TRAIN modes. + * + * @mrc_params: host structure for all MRC global data + * @addr: memory adress being tested (must hit specific channel/rank) + * @first_run: if set then the HTE registers are configured, otherwise it is + * assumed configuration is done and we just re-run the test + * @mode: READ_TRAIN or WRITE_TRAIN (the difference is in the pattern) + * + * @return: byte lane failure on each bit (for Quark only bit0 and bit1) + */ +u16 hte_basic_write_read(struct mrc_params *mrc_params, u32 addr, + u8 first_run, u8 mode) +{ + u16 errors; + + ENTERFN(); + + /* Enable all error reporting in preparation for HTE test */ + hte_enable_all_errors(); + hte_clear_error_regs(); + + errors = hte_basic_data_cmp(mrc_params, addr, first_run, mode); + + LEAVEFN(); + + return errors; +} + +/** + * Examine a single-cache-line memory with write/read/verify test using multiple + * data patterns (victim-aggressor algorithm). + * + * @mrc_params: host structure for all MRC global data + * @addr: memory adress being tested (must hit specific channel/rank) + * @first_run: if set then the HTE registers are configured, otherwise it is + * assumed configuration is done and we just re-run the test + * + * @return: byte lane failure on each bit (for Quark only bit0 and bit1) + */ +u16 hte_write_stress_bit_lanes(struct mrc_params *mrc_params, + u32 addr, u8 first_run) +{ + u16 errors; + u8 victim_bit = 0; + + ENTERFN(); + + /* Enable all error reporting in preparation for HTE test */ + hte_enable_all_errors(); + hte_clear_error_regs(); + + /* + * Loop through each bit in the bytelane. + * + * Each pass creates a victim bit while keeping all other bits the same + * as aggressors. AVN HTE adds an auto-rotate feature which allows us + * to program the entire victim/aggressor sequence in 1 step. + * + * The victim bit rotates on each pass so no need to have software + * implement a victim bit loop like on VLV. + */ + errors = hte_rw_data_cmp(mrc_params, addr, HTE_LOOP_CNT, + HTE_LFSR_VICTIM_SEED, HTE_LFSR_AGRESSOR_SEED, + victim_bit, first_run); + + LEAVEFN(); + + return errors; +} + +/** + * Execute a basic single-cache-line memory write or read. + * This is just for receive enable / fine write-levelling purpose. + * + * @addr: memory adress being tested (must hit specific channel/rank) + * @first_run: if set then the HTE registers are configured, otherwise it is + * assumed configuration is done and we just re-run the test + * @is_write: when non-zero memory write operation executed, otherwise read + */ +void hte_mem_op(u32 addr, u8 first_run, u8 is_write) +{ + u32 offset; + u32 tmp; + + hte_enable_all_errors(); + hte_clear_error_regs(); + + if (first_run) { + tmp = is_write ? 0x01110021 : 0x01010021; + msg_port_write(HTE, 0x00020020, tmp); + + msg_port_write(HTE, 0x00020021, 0x06000000); + msg_port_write(HTE, 0x00020022, addr >> 6); + msg_port_write(HTE, 0x00020062, 0x00800015); + msg_port_write(HTE, 0x00020063, 0xAAAAAAAA); + msg_port_write(HTE, 0x00020064, 0xCCCCCCCC); + msg_port_write(HTE, 0x00020065, 0xF0F0F0F0); + msg_port_write(HTE, 0x00020061, 0x00030008); + + for (offset = 0x80; offset <= 0x8F; offset++) + msg_port_write(HTE, offset, 0xC33C0000); + } + + msg_port_write(HTE, 0x000200A1, 0xFFFF1000); + msg_port_write(HTE, 0x00020011, 0x00011000); + msg_port_write(HTE, 0x00020011, 0x00011100); + + hte_wait_for_complete(); +} |