138ad43e4SBin Meng /* 238ad43e4SBin Meng * Copyright (C) 2013, Intel Corporation 338ad43e4SBin Meng * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> 438ad43e4SBin Meng * 538ad43e4SBin Meng * Ported from Intel released Quark UEFI BIOS 638ad43e4SBin Meng * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei 738ad43e4SBin Meng * 838ad43e4SBin Meng * SPDX-License-Identifier: Intel 938ad43e4SBin Meng */ 1038ad43e4SBin Meng 1138ad43e4SBin Meng #include <common.h> 1238ad43e4SBin Meng #include <asm/arch/device.h> 1338ad43e4SBin Meng #include <asm/arch/mrc.h> 1438ad43e4SBin Meng #include <asm/arch/msg_port.h> 1538ad43e4SBin Meng #include "mrc_util.h" 1638ad43e4SBin Meng #include "hte.h" 1738ad43e4SBin Meng #include "smc.h" 1838ad43e4SBin Meng 1938ad43e4SBin Meng static const uint8_t vref_codes[64] = { 2038ad43e4SBin Meng /* lowest to highest */ 21*312cc39eSBin Meng 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 2238ad43e4SBin Meng 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 23*312cc39eSBin Meng 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 2438ad43e4SBin Meng 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 2538ad43e4SBin Meng 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 26*312cc39eSBin Meng 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 2738ad43e4SBin Meng 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 28*312cc39eSBin Meng 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f 2938ad43e4SBin Meng }; 3038ad43e4SBin Meng 3138ad43e4SBin Meng void mrc_write_mask(u32 unit, u32 addr, u32 data, u32 mask) 3238ad43e4SBin Meng { 3338ad43e4SBin Meng msg_port_write(unit, addr, 3438ad43e4SBin Meng (msg_port_read(unit, addr) & ~(mask)) | 3538ad43e4SBin Meng ((data) & (mask))); 3638ad43e4SBin Meng } 3738ad43e4SBin Meng 3838ad43e4SBin Meng void mrc_alt_write_mask(u32 unit, u32 addr, u32 data, u32 mask) 3938ad43e4SBin Meng { 4038ad43e4SBin Meng msg_port_alt_write(unit, addr, 4138ad43e4SBin Meng (msg_port_alt_read(unit, addr) & ~(mask)) | 4238ad43e4SBin Meng ((data) & (mask))); 4338ad43e4SBin Meng } 4438ad43e4SBin Meng 4538ad43e4SBin Meng void mrc_post_code(uint8_t major, uint8_t minor) 4638ad43e4SBin Meng { 4738ad43e4SBin Meng /* send message to UART */ 4838ad43e4SBin Meng DPF(D_INFO, "POST: 0x%01x%02x\n", major, minor); 4938ad43e4SBin Meng 5038ad43e4SBin Meng /* error check */ 5138ad43e4SBin Meng if (major == 0xee) 5238ad43e4SBin Meng hang(); 5338ad43e4SBin Meng } 5438ad43e4SBin Meng 5538ad43e4SBin Meng /* Delay number of nanoseconds */ 5638ad43e4SBin Meng void delay_n(uint32_t ns) 5738ad43e4SBin Meng { 5838ad43e4SBin Meng /* 1000 MHz clock has 1ns period --> no conversion required */ 5938ad43e4SBin Meng uint64_t final_tsc = rdtsc(); 6038ad43e4SBin Meng 6138ad43e4SBin Meng final_tsc += ((get_tbclk_mhz() * ns) / 1000); 6238ad43e4SBin Meng 6338ad43e4SBin Meng while (rdtsc() < final_tsc) 6438ad43e4SBin Meng ; 6538ad43e4SBin Meng } 6638ad43e4SBin Meng 6738ad43e4SBin Meng /* Delay number of microseconds */ 6838ad43e4SBin Meng void delay_u(uint32_t ms) 6938ad43e4SBin Meng { 7038ad43e4SBin Meng /* 64-bit math is not an option, just use loops */ 7138ad43e4SBin Meng while (ms--) 7238ad43e4SBin Meng delay_n(1000); 7338ad43e4SBin Meng } 7438ad43e4SBin Meng 7538ad43e4SBin Meng /* Select Memory Manager as the source for PRI interface */ 7638ad43e4SBin Meng void select_mem_mgr(void) 7738ad43e4SBin Meng { 7838ad43e4SBin Meng u32 dco; 7938ad43e4SBin Meng 8038ad43e4SBin Meng ENTERFN(); 8138ad43e4SBin Meng 8238ad43e4SBin Meng dco = msg_port_read(MEM_CTLR, DCO); 83*312cc39eSBin Meng dco &= ~DCO_PMICTL; 8438ad43e4SBin Meng msg_port_write(MEM_CTLR, DCO, dco); 8538ad43e4SBin Meng 8638ad43e4SBin Meng LEAVEFN(); 8738ad43e4SBin Meng } 8838ad43e4SBin Meng 8938ad43e4SBin Meng /* Select HTE as the source for PRI interface */ 9038ad43e4SBin Meng void select_hte(void) 9138ad43e4SBin Meng { 9238ad43e4SBin Meng u32 dco; 9338ad43e4SBin Meng 9438ad43e4SBin Meng ENTERFN(); 9538ad43e4SBin Meng 9638ad43e4SBin Meng dco = msg_port_read(MEM_CTLR, DCO); 97*312cc39eSBin Meng dco |= DCO_PMICTL; 9838ad43e4SBin Meng msg_port_write(MEM_CTLR, DCO, dco); 9938ad43e4SBin Meng 10038ad43e4SBin Meng LEAVEFN(); 10138ad43e4SBin Meng } 10238ad43e4SBin Meng 10338ad43e4SBin Meng /* 10438ad43e4SBin Meng * Send DRAM command 10538ad43e4SBin Meng * data should be formated using DCMD_Xxxx macro or emrsXCommand structure 10638ad43e4SBin Meng */ 10738ad43e4SBin Meng void dram_init_command(uint32_t data) 10838ad43e4SBin Meng { 10938ad43e4SBin Meng pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, data); 11038ad43e4SBin Meng pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG, 0); 11138ad43e4SBin Meng msg_port_setup(MSG_OP_DRAM_INIT, MEM_CTLR, 0); 11238ad43e4SBin Meng 11338ad43e4SBin Meng DPF(D_REGWR, "WR32 %03X %08X %08X\n", MEM_CTLR, 0, data); 11438ad43e4SBin Meng } 11538ad43e4SBin Meng 11638ad43e4SBin Meng /* Send DRAM wake command using special MCU side-band WAKE opcode */ 11738ad43e4SBin Meng void dram_wake_command(void) 11838ad43e4SBin Meng { 11938ad43e4SBin Meng ENTERFN(); 12038ad43e4SBin Meng 12138ad43e4SBin Meng msg_port_setup(MSG_OP_DRAM_WAKE, MEM_CTLR, 0); 12238ad43e4SBin Meng 12338ad43e4SBin Meng LEAVEFN(); 12438ad43e4SBin Meng } 12538ad43e4SBin Meng 12638ad43e4SBin Meng void training_message(uint8_t channel, uint8_t rank, uint8_t byte_lane) 12738ad43e4SBin Meng { 12838ad43e4SBin Meng /* send message to UART */ 12938ad43e4SBin Meng DPF(D_INFO, "CH%01X RK%01X BL%01X\n", channel, rank, byte_lane); 13038ad43e4SBin Meng } 13138ad43e4SBin Meng 13238ad43e4SBin Meng /* 13338ad43e4SBin Meng * This function will program the RCVEN delays 13438ad43e4SBin Meng * 13538ad43e4SBin Meng * (currently doesn't comprehend rank) 13638ad43e4SBin Meng */ 13738ad43e4SBin Meng void set_rcvn(uint8_t channel, uint8_t rank, 13838ad43e4SBin Meng uint8_t byte_lane, uint32_t pi_count) 13938ad43e4SBin Meng { 14038ad43e4SBin Meng uint32_t reg; 14138ad43e4SBin Meng uint32_t msk; 14238ad43e4SBin Meng uint32_t temp; 14338ad43e4SBin Meng 14438ad43e4SBin Meng ENTERFN(); 14538ad43e4SBin Meng 14638ad43e4SBin Meng DPF(D_TRN, "Rcvn ch%d rnk%d ln%d : pi=%03X\n", 14738ad43e4SBin Meng channel, rank, byte_lane, pi_count); 14838ad43e4SBin Meng 14938ad43e4SBin Meng /* 15038ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 15138ad43e4SBin Meng * BL0 -> B01PTRCTL0[11:08] (0x0-0xF) 15238ad43e4SBin Meng * BL1 -> B01PTRCTL0[23:20] (0x0-0xF) 15338ad43e4SBin Meng */ 154*312cc39eSBin Meng reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 155*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 156*312cc39eSBin Meng msk = (byte_lane & 1) ? 0xf00000 : 0xf00; 157*312cc39eSBin Meng temp = (byte_lane & 1) ? (pi_count / HALF_CLK) << 20 : 158*312cc39eSBin Meng (pi_count / HALF_CLK) << 8; 15938ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 16038ad43e4SBin Meng 16138ad43e4SBin Meng /* Adjust PI_COUNT */ 162*312cc39eSBin Meng pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; 16338ad43e4SBin Meng 16438ad43e4SBin Meng /* 16538ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 16638ad43e4SBin Meng * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F) 16738ad43e4SBin Meng * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F) 16838ad43e4SBin Meng */ 169*312cc39eSBin Meng reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; 170*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 171*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 172*312cc39eSBin Meng msk = 0x3f000000; 17338ad43e4SBin Meng temp = pi_count << 24; 17438ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 17538ad43e4SBin Meng 17638ad43e4SBin Meng /* 17738ad43e4SBin Meng * DEADBAND 17838ad43e4SBin Meng * BL0/1 -> B01DBCTL1[08/11] (+1 select) 17938ad43e4SBin Meng * BL0/1 -> B01DBCTL1[02/05] (enable) 18038ad43e4SBin Meng */ 181*312cc39eSBin Meng reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 182*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 18338ad43e4SBin Meng msk = 0x00; 18438ad43e4SBin Meng temp = 0x00; 18538ad43e4SBin Meng 18638ad43e4SBin Meng /* enable */ 187*312cc39eSBin Meng msk |= (byte_lane & 1) ? (1 << 5) : (1 << 2); 18838ad43e4SBin Meng if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) 18938ad43e4SBin Meng temp |= msk; 19038ad43e4SBin Meng 19138ad43e4SBin Meng /* select */ 192*312cc39eSBin Meng msk |= (byte_lane & 1) ? (1 << 11) : (1 << 8); 19338ad43e4SBin Meng if (pi_count < EARLY_DB) 19438ad43e4SBin Meng temp |= msk; 19538ad43e4SBin Meng 19638ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 19738ad43e4SBin Meng 19838ad43e4SBin Meng /* error check */ 199*312cc39eSBin Meng if (pi_count > 0x3f) { 20038ad43e4SBin Meng training_message(channel, rank, byte_lane); 20138ad43e4SBin Meng mrc_post_code(0xee, 0xe0); 20238ad43e4SBin Meng } 20338ad43e4SBin Meng 20438ad43e4SBin Meng LEAVEFN(); 20538ad43e4SBin Meng } 20638ad43e4SBin Meng 20738ad43e4SBin Meng /* 20838ad43e4SBin Meng * This function will return the current RCVEN delay on the given 20938ad43e4SBin Meng * channel, rank, byte_lane as an absolute PI count. 21038ad43e4SBin Meng * 21138ad43e4SBin Meng * (currently doesn't comprehend rank) 21238ad43e4SBin Meng */ 21338ad43e4SBin Meng uint32_t get_rcvn(uint8_t channel, uint8_t rank, uint8_t byte_lane) 21438ad43e4SBin Meng { 21538ad43e4SBin Meng uint32_t reg; 21638ad43e4SBin Meng uint32_t temp; 21738ad43e4SBin Meng uint32_t pi_count; 21838ad43e4SBin Meng 21938ad43e4SBin Meng ENTERFN(); 22038ad43e4SBin Meng 22138ad43e4SBin Meng /* 22238ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 22338ad43e4SBin Meng * BL0 -> B01PTRCTL0[11:08] (0x0-0xF) 22438ad43e4SBin Meng * BL1 -> B01PTRCTL0[23:20] (0x0-0xF) 22538ad43e4SBin Meng */ 226*312cc39eSBin Meng reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 227*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 22838ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 229*312cc39eSBin Meng temp >>= (byte_lane & 1) ? 20 : 8; 230*312cc39eSBin Meng temp &= 0xf; 23138ad43e4SBin Meng 23238ad43e4SBin Meng /* Adjust PI_COUNT */ 23338ad43e4SBin Meng pi_count = temp * HALF_CLK; 23438ad43e4SBin Meng 23538ad43e4SBin Meng /* 23638ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 23738ad43e4SBin Meng * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F) 23838ad43e4SBin Meng * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F) 23938ad43e4SBin Meng */ 240*312cc39eSBin Meng reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; 241*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 242*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 24338ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 24438ad43e4SBin Meng temp >>= 24; 245*312cc39eSBin Meng temp &= 0x3f; 24638ad43e4SBin Meng 24738ad43e4SBin Meng /* Adjust PI_COUNT */ 24838ad43e4SBin Meng pi_count += temp; 24938ad43e4SBin Meng 25038ad43e4SBin Meng LEAVEFN(); 25138ad43e4SBin Meng 25238ad43e4SBin Meng return pi_count; 25338ad43e4SBin Meng } 25438ad43e4SBin Meng 25538ad43e4SBin Meng /* 25638ad43e4SBin Meng * This function will program the RDQS delays based on an absolute 25738ad43e4SBin Meng * amount of PIs. 25838ad43e4SBin Meng * 25938ad43e4SBin Meng * (currently doesn't comprehend rank) 26038ad43e4SBin Meng */ 26138ad43e4SBin Meng void set_rdqs(uint8_t channel, uint8_t rank, 26238ad43e4SBin Meng uint8_t byte_lane, uint32_t pi_count) 26338ad43e4SBin Meng { 26438ad43e4SBin Meng uint32_t reg; 26538ad43e4SBin Meng uint32_t msk; 26638ad43e4SBin Meng uint32_t temp; 26738ad43e4SBin Meng 26838ad43e4SBin Meng ENTERFN(); 26938ad43e4SBin Meng DPF(D_TRN, "Rdqs ch%d rnk%d ln%d : pi=%03X\n", 27038ad43e4SBin Meng channel, rank, byte_lane, pi_count); 27138ad43e4SBin Meng 27238ad43e4SBin Meng /* 27338ad43e4SBin Meng * PI (1/128 MCLK) 27438ad43e4SBin Meng * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47) 27538ad43e4SBin Meng * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47) 27638ad43e4SBin Meng */ 277*312cc39eSBin Meng reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE; 278*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 279*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 280*312cc39eSBin Meng msk = 0x7f; 28138ad43e4SBin Meng temp = pi_count << 0; 28238ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 28338ad43e4SBin Meng 28438ad43e4SBin Meng /* error check (shouldn't go above 0x3F) */ 28538ad43e4SBin Meng if (pi_count > 0x47) { 28638ad43e4SBin Meng training_message(channel, rank, byte_lane); 28738ad43e4SBin Meng mrc_post_code(0xee, 0xe1); 28838ad43e4SBin Meng } 28938ad43e4SBin Meng 29038ad43e4SBin Meng LEAVEFN(); 29138ad43e4SBin Meng } 29238ad43e4SBin Meng 29338ad43e4SBin Meng /* 29438ad43e4SBin Meng * This function will return the current RDQS delay on the given 29538ad43e4SBin Meng * channel, rank, byte_lane as an absolute PI count. 29638ad43e4SBin Meng * 29738ad43e4SBin Meng * (currently doesn't comprehend rank) 29838ad43e4SBin Meng */ 29938ad43e4SBin Meng uint32_t get_rdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane) 30038ad43e4SBin Meng { 30138ad43e4SBin Meng uint32_t reg; 30238ad43e4SBin Meng uint32_t temp; 30338ad43e4SBin Meng uint32_t pi_count; 30438ad43e4SBin Meng 30538ad43e4SBin Meng ENTERFN(); 30638ad43e4SBin Meng 30738ad43e4SBin Meng /* 30838ad43e4SBin Meng * PI (1/128 MCLK) 30938ad43e4SBin Meng * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47) 31038ad43e4SBin Meng * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47) 31138ad43e4SBin Meng */ 312*312cc39eSBin Meng reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE; 313*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 314*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 31538ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 31638ad43e4SBin Meng 31738ad43e4SBin Meng /* Adjust PI_COUNT */ 318*312cc39eSBin Meng pi_count = temp & 0x7f; 31938ad43e4SBin Meng 32038ad43e4SBin Meng LEAVEFN(); 32138ad43e4SBin Meng 32238ad43e4SBin Meng return pi_count; 32338ad43e4SBin Meng } 32438ad43e4SBin Meng 32538ad43e4SBin Meng /* 32638ad43e4SBin Meng * This function will program the WDQS delays based on an absolute 32738ad43e4SBin Meng * amount of PIs. 32838ad43e4SBin Meng * 32938ad43e4SBin Meng * (currently doesn't comprehend rank) 33038ad43e4SBin Meng */ 33138ad43e4SBin Meng void set_wdqs(uint8_t channel, uint8_t rank, 33238ad43e4SBin Meng uint8_t byte_lane, uint32_t pi_count) 33338ad43e4SBin Meng { 33438ad43e4SBin Meng uint32_t reg; 33538ad43e4SBin Meng uint32_t msk; 33638ad43e4SBin Meng uint32_t temp; 33738ad43e4SBin Meng 33838ad43e4SBin Meng ENTERFN(); 33938ad43e4SBin Meng 34038ad43e4SBin Meng DPF(D_TRN, "Wdqs ch%d rnk%d ln%d : pi=%03X\n", 34138ad43e4SBin Meng channel, rank, byte_lane, pi_count); 34238ad43e4SBin Meng 34338ad43e4SBin Meng /* 34438ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 34538ad43e4SBin Meng * BL0 -> B01PTRCTL0[07:04] (0x0-0xF) 34638ad43e4SBin Meng * BL1 -> B01PTRCTL0[19:16] (0x0-0xF) 34738ad43e4SBin Meng */ 348*312cc39eSBin Meng reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 349*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 350*312cc39eSBin Meng msk = (byte_lane & 1) ? 0xf0000 : 0xf0; 35138ad43e4SBin Meng temp = pi_count / HALF_CLK; 352*312cc39eSBin Meng temp <<= (byte_lane & 1) ? 16 : 4; 35338ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 35438ad43e4SBin Meng 35538ad43e4SBin Meng /* Adjust PI_COUNT */ 356*312cc39eSBin Meng pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; 35738ad43e4SBin Meng 35838ad43e4SBin Meng /* 35938ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 36038ad43e4SBin Meng * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F) 36138ad43e4SBin Meng * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F) 36238ad43e4SBin Meng */ 363*312cc39eSBin Meng reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; 364*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 365*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 366*312cc39eSBin Meng msk = 0x3f0000; 36738ad43e4SBin Meng temp = pi_count << 16; 36838ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 36938ad43e4SBin Meng 37038ad43e4SBin Meng /* 37138ad43e4SBin Meng * DEADBAND 37238ad43e4SBin Meng * BL0/1 -> B01DBCTL1[07/10] (+1 select) 37338ad43e4SBin Meng * BL0/1 -> B01DBCTL1[01/04] (enable) 37438ad43e4SBin Meng */ 375*312cc39eSBin Meng reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 376*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 37738ad43e4SBin Meng msk = 0x00; 37838ad43e4SBin Meng temp = 0x00; 37938ad43e4SBin Meng 38038ad43e4SBin Meng /* enable */ 381*312cc39eSBin Meng msk |= (byte_lane & 1) ? (1 << 4) : (1 << 1); 38238ad43e4SBin Meng if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) 38338ad43e4SBin Meng temp |= msk; 38438ad43e4SBin Meng 38538ad43e4SBin Meng /* select */ 386*312cc39eSBin Meng msk |= (byte_lane & 1) ? (1 << 10) : (1 << 7); 38738ad43e4SBin Meng if (pi_count < EARLY_DB) 38838ad43e4SBin Meng temp |= msk; 38938ad43e4SBin Meng 39038ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 39138ad43e4SBin Meng 39238ad43e4SBin Meng /* error check */ 393*312cc39eSBin Meng if (pi_count > 0x3f) { 39438ad43e4SBin Meng training_message(channel, rank, byte_lane); 39538ad43e4SBin Meng mrc_post_code(0xee, 0xe2); 39638ad43e4SBin Meng } 39738ad43e4SBin Meng 39838ad43e4SBin Meng LEAVEFN(); 39938ad43e4SBin Meng } 40038ad43e4SBin Meng 40138ad43e4SBin Meng /* 40238ad43e4SBin Meng * This function will return the amount of WDQS delay on the given 40338ad43e4SBin Meng * channel, rank, byte_lane as an absolute PI count. 40438ad43e4SBin Meng * 40538ad43e4SBin Meng * (currently doesn't comprehend rank) 40638ad43e4SBin Meng */ 40738ad43e4SBin Meng uint32_t get_wdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane) 40838ad43e4SBin Meng { 40938ad43e4SBin Meng uint32_t reg; 41038ad43e4SBin Meng uint32_t temp; 41138ad43e4SBin Meng uint32_t pi_count; 41238ad43e4SBin Meng 41338ad43e4SBin Meng ENTERFN(); 41438ad43e4SBin Meng 41538ad43e4SBin Meng /* 41638ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 41738ad43e4SBin Meng * BL0 -> B01PTRCTL0[07:04] (0x0-0xF) 41838ad43e4SBin Meng * BL1 -> B01PTRCTL0[19:16] (0x0-0xF) 41938ad43e4SBin Meng */ 420*312cc39eSBin Meng reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 421*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 42238ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 423*312cc39eSBin Meng temp >>= (byte_lane & 1) ? 16 : 4; 424*312cc39eSBin Meng temp &= 0xf; 42538ad43e4SBin Meng 42638ad43e4SBin Meng /* Adjust PI_COUNT */ 42738ad43e4SBin Meng pi_count = (temp * HALF_CLK); 42838ad43e4SBin Meng 42938ad43e4SBin Meng /* 43038ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 43138ad43e4SBin Meng * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F) 43238ad43e4SBin Meng * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F) 43338ad43e4SBin Meng */ 434*312cc39eSBin Meng reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; 435*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 436*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 43738ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 43838ad43e4SBin Meng temp >>= 16; 439*312cc39eSBin Meng temp &= 0x3f; 44038ad43e4SBin Meng 44138ad43e4SBin Meng /* Adjust PI_COUNT */ 44238ad43e4SBin Meng pi_count += temp; 44338ad43e4SBin Meng 44438ad43e4SBin Meng LEAVEFN(); 44538ad43e4SBin Meng 44638ad43e4SBin Meng return pi_count; 44738ad43e4SBin Meng } 44838ad43e4SBin Meng 44938ad43e4SBin Meng /* 45038ad43e4SBin Meng * This function will program the WDQ delays based on an absolute 45138ad43e4SBin Meng * number of PIs. 45238ad43e4SBin Meng * 45338ad43e4SBin Meng * (currently doesn't comprehend rank) 45438ad43e4SBin Meng */ 45538ad43e4SBin Meng void set_wdq(uint8_t channel, uint8_t rank, 45638ad43e4SBin Meng uint8_t byte_lane, uint32_t pi_count) 45738ad43e4SBin Meng { 45838ad43e4SBin Meng uint32_t reg; 45938ad43e4SBin Meng uint32_t msk; 46038ad43e4SBin Meng uint32_t temp; 46138ad43e4SBin Meng 46238ad43e4SBin Meng ENTERFN(); 46338ad43e4SBin Meng 46438ad43e4SBin Meng DPF(D_TRN, "Wdq ch%d rnk%d ln%d : pi=%03X\n", 46538ad43e4SBin Meng channel, rank, byte_lane, pi_count); 46638ad43e4SBin Meng 46738ad43e4SBin Meng /* 46838ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 46938ad43e4SBin Meng * BL0 -> B01PTRCTL0[03:00] (0x0-0xF) 47038ad43e4SBin Meng * BL1 -> B01PTRCTL0[15:12] (0x0-0xF) 47138ad43e4SBin Meng */ 472*312cc39eSBin Meng reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 473*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 474*312cc39eSBin Meng msk = (byte_lane & 1) ? 0xf000 : 0xf; 47538ad43e4SBin Meng temp = pi_count / HALF_CLK; 476*312cc39eSBin Meng temp <<= (byte_lane & 1) ? 12 : 0; 47738ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 47838ad43e4SBin Meng 47938ad43e4SBin Meng /* Adjust PI_COUNT */ 480*312cc39eSBin Meng pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; 48138ad43e4SBin Meng 48238ad43e4SBin Meng /* 48338ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 48438ad43e4SBin Meng * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F) 48538ad43e4SBin Meng * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F) 48638ad43e4SBin Meng */ 487*312cc39eSBin Meng reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; 488*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 489*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 490*312cc39eSBin Meng msk = 0x3f00; 49138ad43e4SBin Meng temp = pi_count << 8; 49238ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 49338ad43e4SBin Meng 49438ad43e4SBin Meng /* 49538ad43e4SBin Meng * DEADBAND 49638ad43e4SBin Meng * BL0/1 -> B01DBCTL1[06/09] (+1 select) 49738ad43e4SBin Meng * BL0/1 -> B01DBCTL1[00/03] (enable) 49838ad43e4SBin Meng */ 499*312cc39eSBin Meng reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 500*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 50138ad43e4SBin Meng msk = 0x00; 50238ad43e4SBin Meng temp = 0x00; 50338ad43e4SBin Meng 50438ad43e4SBin Meng /* enable */ 505*312cc39eSBin Meng msk |= (byte_lane & 1) ? (1 << 3) : (1 << 0); 50638ad43e4SBin Meng if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) 50738ad43e4SBin Meng temp |= msk; 50838ad43e4SBin Meng 50938ad43e4SBin Meng /* select */ 510*312cc39eSBin Meng msk |= (byte_lane & 1) ? (1 << 9) : (1 << 6); 51138ad43e4SBin Meng if (pi_count < EARLY_DB) 51238ad43e4SBin Meng temp |= msk; 51338ad43e4SBin Meng 51438ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 51538ad43e4SBin Meng 51638ad43e4SBin Meng /* error check */ 517*312cc39eSBin Meng if (pi_count > 0x3f) { 51838ad43e4SBin Meng training_message(channel, rank, byte_lane); 51938ad43e4SBin Meng mrc_post_code(0xee, 0xe3); 52038ad43e4SBin Meng } 52138ad43e4SBin Meng 52238ad43e4SBin Meng LEAVEFN(); 52338ad43e4SBin Meng } 52438ad43e4SBin Meng 52538ad43e4SBin Meng /* 52638ad43e4SBin Meng * This function will return the amount of WDQ delay on the given 52738ad43e4SBin Meng * channel, rank, byte_lane as an absolute PI count. 52838ad43e4SBin Meng * 52938ad43e4SBin Meng * (currently doesn't comprehend rank) 53038ad43e4SBin Meng */ 53138ad43e4SBin Meng uint32_t get_wdq(uint8_t channel, uint8_t rank, uint8_t byte_lane) 53238ad43e4SBin Meng { 53338ad43e4SBin Meng uint32_t reg; 53438ad43e4SBin Meng uint32_t temp; 53538ad43e4SBin Meng uint32_t pi_count; 53638ad43e4SBin Meng 53738ad43e4SBin Meng ENTERFN(); 53838ad43e4SBin Meng 53938ad43e4SBin Meng /* 54038ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 54138ad43e4SBin Meng * BL0 -> B01PTRCTL0[03:00] (0x0-0xF) 54238ad43e4SBin Meng * BL1 -> B01PTRCTL0[15:12] (0x0-0xF) 54338ad43e4SBin Meng */ 544*312cc39eSBin Meng reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + 545*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET; 54638ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 547*312cc39eSBin Meng temp >>= (byte_lane & 1) ? 12 : 0; 548*312cc39eSBin Meng temp &= 0xf; 54938ad43e4SBin Meng 55038ad43e4SBin Meng /* Adjust PI_COUNT */ 55138ad43e4SBin Meng pi_count = temp * HALF_CLK; 55238ad43e4SBin Meng 55338ad43e4SBin Meng /* 55438ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 55538ad43e4SBin Meng * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F) 55638ad43e4SBin Meng * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F) 55738ad43e4SBin Meng */ 558*312cc39eSBin Meng reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; 559*312cc39eSBin Meng reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + 560*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 56138ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 56238ad43e4SBin Meng temp >>= 8; 563*312cc39eSBin Meng temp &= 0x3f; 56438ad43e4SBin Meng 56538ad43e4SBin Meng /* Adjust PI_COUNT */ 56638ad43e4SBin Meng pi_count += temp; 56738ad43e4SBin Meng 56838ad43e4SBin Meng LEAVEFN(); 56938ad43e4SBin Meng 57038ad43e4SBin Meng return pi_count; 57138ad43e4SBin Meng } 57238ad43e4SBin Meng 57338ad43e4SBin Meng /* 57438ad43e4SBin Meng * This function will program the WCMD delays based on an absolute 57538ad43e4SBin Meng * number of PIs. 57638ad43e4SBin Meng */ 57738ad43e4SBin Meng void set_wcmd(uint8_t channel, uint32_t pi_count) 57838ad43e4SBin Meng { 57938ad43e4SBin Meng uint32_t reg; 58038ad43e4SBin Meng uint32_t msk; 58138ad43e4SBin Meng uint32_t temp; 58238ad43e4SBin Meng 58338ad43e4SBin Meng ENTERFN(); 58438ad43e4SBin Meng 58538ad43e4SBin Meng /* 58638ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 58738ad43e4SBin Meng * CMDPTRREG[11:08] (0x0-0xF) 58838ad43e4SBin Meng */ 589*312cc39eSBin Meng reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET; 590*312cc39eSBin Meng msk = 0xf00; 59138ad43e4SBin Meng temp = pi_count / HALF_CLK; 59238ad43e4SBin Meng temp <<= 8; 59338ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 59438ad43e4SBin Meng 59538ad43e4SBin Meng /* Adjust PI_COUNT */ 596*312cc39eSBin Meng pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; 59738ad43e4SBin Meng 59838ad43e4SBin Meng /* 59938ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 60038ad43e4SBin Meng * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused) 60138ad43e4SBin Meng * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused) 60238ad43e4SBin Meng * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused) 60338ad43e4SBin Meng * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused) 60438ad43e4SBin Meng * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused) 60538ad43e4SBin Meng * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F) 60638ad43e4SBin Meng * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused) 60738ad43e4SBin Meng * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused) 60838ad43e4SBin Meng */ 609*312cc39eSBin Meng reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET; 610*312cc39eSBin Meng msk = 0x3f3f3f3f; 61138ad43e4SBin Meng temp = (pi_count << 24) | (pi_count << 16) | 61238ad43e4SBin Meng (pi_count << 8) | (pi_count << 0); 61338ad43e4SBin Meng 61438ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 615*312cc39eSBin Meng reg = CMDDLLPICODER0 + channel * DDRIOCCC_CH_OFFSET; /* PO */ 61638ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 61738ad43e4SBin Meng 61838ad43e4SBin Meng /* 61938ad43e4SBin Meng * DEADBAND 62038ad43e4SBin Meng * CMDCFGREG0[17] (+1 select) 62138ad43e4SBin Meng * CMDCFGREG0[16] (enable) 62238ad43e4SBin Meng */ 623*312cc39eSBin Meng reg = CMDCFGREG0 + channel * DDRIOCCC_CH_OFFSET; 62438ad43e4SBin Meng msk = 0x00; 62538ad43e4SBin Meng temp = 0x00; 62638ad43e4SBin Meng 62738ad43e4SBin Meng /* enable */ 628*312cc39eSBin Meng msk |= (1 << 16); 62938ad43e4SBin Meng if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) 63038ad43e4SBin Meng temp |= msk; 63138ad43e4SBin Meng 63238ad43e4SBin Meng /* select */ 633*312cc39eSBin Meng msk |= (1 << 17); 63438ad43e4SBin Meng if (pi_count < EARLY_DB) 63538ad43e4SBin Meng temp |= msk; 63638ad43e4SBin Meng 63738ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 63838ad43e4SBin Meng 63938ad43e4SBin Meng /* error check */ 640*312cc39eSBin Meng if (pi_count > 0x3f) 64138ad43e4SBin Meng mrc_post_code(0xee, 0xe4); 64238ad43e4SBin Meng 64338ad43e4SBin Meng LEAVEFN(); 64438ad43e4SBin Meng } 64538ad43e4SBin Meng 64638ad43e4SBin Meng /* 64738ad43e4SBin Meng * This function will return the amount of WCMD delay on the given 64838ad43e4SBin Meng * channel as an absolute PI count. 64938ad43e4SBin Meng */ 65038ad43e4SBin Meng uint32_t get_wcmd(uint8_t channel) 65138ad43e4SBin Meng { 65238ad43e4SBin Meng uint32_t reg; 65338ad43e4SBin Meng uint32_t temp; 65438ad43e4SBin Meng uint32_t pi_count; 65538ad43e4SBin Meng 65638ad43e4SBin Meng ENTERFN(); 65738ad43e4SBin Meng 65838ad43e4SBin Meng /* 65938ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 66038ad43e4SBin Meng * CMDPTRREG[11:08] (0x0-0xF) 66138ad43e4SBin Meng */ 662*312cc39eSBin Meng reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET; 66338ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 66438ad43e4SBin Meng temp >>= 8; 665*312cc39eSBin Meng temp &= 0xf; 66638ad43e4SBin Meng 66738ad43e4SBin Meng /* Adjust PI_COUNT */ 66838ad43e4SBin Meng pi_count = temp * HALF_CLK; 66938ad43e4SBin Meng 67038ad43e4SBin Meng /* 67138ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 67238ad43e4SBin Meng * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused) 67338ad43e4SBin Meng * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused) 67438ad43e4SBin Meng * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused) 67538ad43e4SBin Meng * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused) 67638ad43e4SBin Meng * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused) 67738ad43e4SBin Meng * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F) 67838ad43e4SBin Meng * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused) 67938ad43e4SBin Meng * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused) 68038ad43e4SBin Meng */ 681*312cc39eSBin Meng reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET; 68238ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 68338ad43e4SBin Meng temp >>= 16; 684*312cc39eSBin Meng temp &= 0x3f; 68538ad43e4SBin Meng 68638ad43e4SBin Meng /* Adjust PI_COUNT */ 68738ad43e4SBin Meng pi_count += temp; 68838ad43e4SBin Meng 68938ad43e4SBin Meng LEAVEFN(); 69038ad43e4SBin Meng 69138ad43e4SBin Meng return pi_count; 69238ad43e4SBin Meng } 69338ad43e4SBin Meng 69438ad43e4SBin Meng /* 69538ad43e4SBin Meng * This function will program the WCLK delays based on an absolute 69638ad43e4SBin Meng * number of PIs. 69738ad43e4SBin Meng */ 69838ad43e4SBin Meng void set_wclk(uint8_t channel, uint8_t rank, uint32_t pi_count) 69938ad43e4SBin Meng { 70038ad43e4SBin Meng uint32_t reg; 70138ad43e4SBin Meng uint32_t msk; 70238ad43e4SBin Meng uint32_t temp; 70338ad43e4SBin Meng 70438ad43e4SBin Meng ENTERFN(); 70538ad43e4SBin Meng 70638ad43e4SBin Meng /* 70738ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 70838ad43e4SBin Meng * CCPTRREG[15:12] -> CLK1 (0x0-0xF) 70938ad43e4SBin Meng * CCPTRREG[11:08] -> CLK0 (0x0-0xF) 71038ad43e4SBin Meng */ 711*312cc39eSBin Meng reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; 712*312cc39eSBin Meng msk = 0xff00; 71338ad43e4SBin Meng temp = ((pi_count / HALF_CLK) << 12) | ((pi_count / HALF_CLK) << 8); 71438ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 71538ad43e4SBin Meng 71638ad43e4SBin Meng /* Adjust PI_COUNT */ 717*312cc39eSBin Meng pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; 71838ad43e4SBin Meng 71938ad43e4SBin Meng /* 72038ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 72138ad43e4SBin Meng * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F) 72238ad43e4SBin Meng * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F) 72338ad43e4SBin Meng */ 72438ad43e4SBin Meng reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0; 72538ad43e4SBin Meng reg += (channel * DDRIOCCC_CH_OFFSET); 726*312cc39eSBin Meng msk = 0x3f3f00; 72738ad43e4SBin Meng temp = (pi_count << 16) | (pi_count << 8); 72838ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 729*312cc39eSBin Meng 73038ad43e4SBin Meng reg = rank ? ECCB1DLLPICODER1 : ECCB1DLLPICODER1; 73138ad43e4SBin Meng reg += (channel * DDRIOCCC_CH_OFFSET); 73238ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 733*312cc39eSBin Meng 73438ad43e4SBin Meng reg = rank ? ECCB1DLLPICODER2 : ECCB1DLLPICODER2; 73538ad43e4SBin Meng reg += (channel * DDRIOCCC_CH_OFFSET); 73638ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 737*312cc39eSBin Meng 73838ad43e4SBin Meng reg = rank ? ECCB1DLLPICODER3 : ECCB1DLLPICODER3; 73938ad43e4SBin Meng reg += (channel * DDRIOCCC_CH_OFFSET); 74038ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 74138ad43e4SBin Meng 74238ad43e4SBin Meng /* 74338ad43e4SBin Meng * DEADBAND 74438ad43e4SBin Meng * CCCFGREG1[11:08] (+1 select) 74538ad43e4SBin Meng * CCCFGREG1[03:00] (enable) 74638ad43e4SBin Meng */ 747*312cc39eSBin Meng reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET; 74838ad43e4SBin Meng msk = 0x00; 74938ad43e4SBin Meng temp = 0x00; 75038ad43e4SBin Meng 75138ad43e4SBin Meng /* enable */ 752*312cc39eSBin Meng msk |= 0xf; 75338ad43e4SBin Meng if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) 75438ad43e4SBin Meng temp |= msk; 75538ad43e4SBin Meng 75638ad43e4SBin Meng /* select */ 757*312cc39eSBin Meng msk |= 0xf00; 75838ad43e4SBin Meng if (pi_count < EARLY_DB) 75938ad43e4SBin Meng temp |= msk; 76038ad43e4SBin Meng 76138ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 76238ad43e4SBin Meng 76338ad43e4SBin Meng /* error check */ 764*312cc39eSBin Meng if (pi_count > 0x3f) 76538ad43e4SBin Meng mrc_post_code(0xee, 0xe5); 76638ad43e4SBin Meng 76738ad43e4SBin Meng LEAVEFN(); 76838ad43e4SBin Meng } 76938ad43e4SBin Meng 77038ad43e4SBin Meng /* 77138ad43e4SBin Meng * This function will return the amout of WCLK delay on the given 77238ad43e4SBin Meng * channel, rank as an absolute PI count. 77338ad43e4SBin Meng */ 77438ad43e4SBin Meng uint32_t get_wclk(uint8_t channel, uint8_t rank) 77538ad43e4SBin Meng { 77638ad43e4SBin Meng uint32_t reg; 77738ad43e4SBin Meng uint32_t temp; 77838ad43e4SBin Meng uint32_t pi_count; 77938ad43e4SBin Meng 78038ad43e4SBin Meng ENTERFN(); 78138ad43e4SBin Meng 78238ad43e4SBin Meng /* 78338ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 78438ad43e4SBin Meng * CCPTRREG[15:12] -> CLK1 (0x0-0xF) 78538ad43e4SBin Meng * CCPTRREG[11:08] -> CLK0 (0x0-0xF) 78638ad43e4SBin Meng */ 787*312cc39eSBin Meng reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; 78838ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 78938ad43e4SBin Meng temp >>= rank ? 12 : 8; 790*312cc39eSBin Meng temp &= 0xf; 79138ad43e4SBin Meng 79238ad43e4SBin Meng /* Adjust PI_COUNT */ 79338ad43e4SBin Meng pi_count = temp * HALF_CLK; 79438ad43e4SBin Meng 79538ad43e4SBin Meng /* 79638ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 79738ad43e4SBin Meng * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F) 79838ad43e4SBin Meng * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F) 79938ad43e4SBin Meng */ 80038ad43e4SBin Meng reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0; 80138ad43e4SBin Meng reg += (channel * DDRIOCCC_CH_OFFSET); 80238ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 80338ad43e4SBin Meng temp >>= rank ? 16 : 8; 804*312cc39eSBin Meng temp &= 0x3f; 80538ad43e4SBin Meng 80638ad43e4SBin Meng pi_count += temp; 80738ad43e4SBin Meng 80838ad43e4SBin Meng LEAVEFN(); 80938ad43e4SBin Meng 81038ad43e4SBin Meng return pi_count; 81138ad43e4SBin Meng } 81238ad43e4SBin Meng 81338ad43e4SBin Meng /* 81438ad43e4SBin Meng * This function will program the WCTL delays based on an absolute 81538ad43e4SBin Meng * number of PIs. 81638ad43e4SBin Meng * 81738ad43e4SBin Meng * (currently doesn't comprehend rank) 81838ad43e4SBin Meng */ 81938ad43e4SBin Meng void set_wctl(uint8_t channel, uint8_t rank, uint32_t pi_count) 82038ad43e4SBin Meng { 82138ad43e4SBin Meng uint32_t reg; 82238ad43e4SBin Meng uint32_t msk; 82338ad43e4SBin Meng uint32_t temp; 82438ad43e4SBin Meng 82538ad43e4SBin Meng ENTERFN(); 82638ad43e4SBin Meng 82738ad43e4SBin Meng /* 82838ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 82938ad43e4SBin Meng * CCPTRREG[31:28] (0x0-0xF) 83038ad43e4SBin Meng * CCPTRREG[27:24] (0x0-0xF) 83138ad43e4SBin Meng */ 832*312cc39eSBin Meng reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; 833*312cc39eSBin Meng msk = 0xff000000; 83438ad43e4SBin Meng temp = ((pi_count / HALF_CLK) << 28) | ((pi_count / HALF_CLK) << 24); 83538ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 83638ad43e4SBin Meng 83738ad43e4SBin Meng /* Adjust PI_COUNT */ 838*312cc39eSBin Meng pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; 83938ad43e4SBin Meng 84038ad43e4SBin Meng /* 84138ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 84238ad43e4SBin Meng * ECCB1DLLPICODER?[29:24] (0x00-0x3F) 84338ad43e4SBin Meng * ECCB1DLLPICODER?[29:24] (0x00-0x3F) 84438ad43e4SBin Meng */ 845*312cc39eSBin Meng reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET; 846*312cc39eSBin Meng msk = 0x3f000000; 84738ad43e4SBin Meng temp = (pi_count << 24); 84838ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 849*312cc39eSBin Meng 850*312cc39eSBin Meng reg = ECCB1DLLPICODER1 + channel * DDRIOCCC_CH_OFFSET; 85138ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 852*312cc39eSBin Meng 853*312cc39eSBin Meng reg = ECCB1DLLPICODER2 + channel * DDRIOCCC_CH_OFFSET; 85438ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 855*312cc39eSBin Meng 856*312cc39eSBin Meng reg = ECCB1DLLPICODER3 + channel * DDRIOCCC_CH_OFFSET; 85738ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 85838ad43e4SBin Meng 85938ad43e4SBin Meng /* 86038ad43e4SBin Meng * DEADBAND 86138ad43e4SBin Meng * CCCFGREG1[13:12] (+1 select) 86238ad43e4SBin Meng * CCCFGREG1[05:04] (enable) 86338ad43e4SBin Meng */ 864*312cc39eSBin Meng reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET; 86538ad43e4SBin Meng msk = 0x00; 86638ad43e4SBin Meng temp = 0x00; 86738ad43e4SBin Meng 86838ad43e4SBin Meng /* enable */ 869*312cc39eSBin Meng msk |= 0x30; 87038ad43e4SBin Meng if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) 87138ad43e4SBin Meng temp |= msk; 87238ad43e4SBin Meng 87338ad43e4SBin Meng /* select */ 874*312cc39eSBin Meng msk |= 0x3000; 87538ad43e4SBin Meng if (pi_count < EARLY_DB) 87638ad43e4SBin Meng temp |= msk; 87738ad43e4SBin Meng 87838ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, reg, temp, msk); 87938ad43e4SBin Meng 88038ad43e4SBin Meng /* error check */ 881*312cc39eSBin Meng if (pi_count > 0x3f) 88238ad43e4SBin Meng mrc_post_code(0xee, 0xe6); 88338ad43e4SBin Meng 88438ad43e4SBin Meng LEAVEFN(); 88538ad43e4SBin Meng } 88638ad43e4SBin Meng 88738ad43e4SBin Meng /* 88838ad43e4SBin Meng * This function will return the amount of WCTL delay on the given 88938ad43e4SBin Meng * channel, rank as an absolute PI count. 89038ad43e4SBin Meng * 89138ad43e4SBin Meng * (currently doesn't comprehend rank) 89238ad43e4SBin Meng */ 89338ad43e4SBin Meng uint32_t get_wctl(uint8_t channel, uint8_t rank) 89438ad43e4SBin Meng { 89538ad43e4SBin Meng uint32_t reg; 89638ad43e4SBin Meng uint32_t temp; 89738ad43e4SBin Meng uint32_t pi_count; 89838ad43e4SBin Meng 89938ad43e4SBin Meng ENTERFN(); 90038ad43e4SBin Meng 90138ad43e4SBin Meng /* 90238ad43e4SBin Meng * RDPTR (1/2 MCLK, 64 PIs) 90338ad43e4SBin Meng * CCPTRREG[31:28] (0x0-0xF) 90438ad43e4SBin Meng * CCPTRREG[27:24] (0x0-0xF) 90538ad43e4SBin Meng */ 906*312cc39eSBin Meng reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; 90738ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 90838ad43e4SBin Meng temp >>= 24; 909*312cc39eSBin Meng temp &= 0xf; 91038ad43e4SBin Meng 91138ad43e4SBin Meng /* Adjust PI_COUNT */ 91238ad43e4SBin Meng pi_count = temp * HALF_CLK; 91338ad43e4SBin Meng 91438ad43e4SBin Meng /* 91538ad43e4SBin Meng * PI (1/64 MCLK, 1 PIs) 91638ad43e4SBin Meng * ECCB1DLLPICODER?[29:24] (0x00-0x3F) 91738ad43e4SBin Meng * ECCB1DLLPICODER?[29:24] (0x00-0x3F) 91838ad43e4SBin Meng */ 919*312cc39eSBin Meng reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET; 92038ad43e4SBin Meng temp = msg_port_alt_read(DDRPHY, reg); 92138ad43e4SBin Meng temp >>= 24; 922*312cc39eSBin Meng temp &= 0x3f; 92338ad43e4SBin Meng 92438ad43e4SBin Meng /* Adjust PI_COUNT */ 92538ad43e4SBin Meng pi_count += temp; 92638ad43e4SBin Meng 92738ad43e4SBin Meng LEAVEFN(); 92838ad43e4SBin Meng 92938ad43e4SBin Meng return pi_count; 93038ad43e4SBin Meng } 93138ad43e4SBin Meng 93238ad43e4SBin Meng /* 93338ad43e4SBin Meng * This function will program the internal Vref setting in a given 93438ad43e4SBin Meng * byte lane in a given channel. 93538ad43e4SBin Meng */ 93638ad43e4SBin Meng void set_vref(uint8_t channel, uint8_t byte_lane, uint32_t setting) 93738ad43e4SBin Meng { 938*312cc39eSBin Meng uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL; 93938ad43e4SBin Meng 94038ad43e4SBin Meng ENTERFN(); 94138ad43e4SBin Meng 94238ad43e4SBin Meng DPF(D_TRN, "Vref ch%d ln%d : val=%03X\n", 94338ad43e4SBin Meng channel, byte_lane, setting); 94438ad43e4SBin Meng 945*312cc39eSBin Meng mrc_alt_write_mask(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET + 946*312cc39eSBin Meng (byte_lane >> 1) * DDRIODQ_BL_OFFSET, 947*312cc39eSBin Meng vref_codes[setting] << 2, 0xfc); 94838ad43e4SBin Meng 94938ad43e4SBin Meng /* 95038ad43e4SBin Meng * need to wait ~300ns for Vref to settle 95138ad43e4SBin Meng * (check that this is necessary) 95238ad43e4SBin Meng */ 95338ad43e4SBin Meng delay_n(300); 95438ad43e4SBin Meng 95538ad43e4SBin Meng /* ??? may need to clear pointers ??? */ 95638ad43e4SBin Meng 95738ad43e4SBin Meng LEAVEFN(); 95838ad43e4SBin Meng } 95938ad43e4SBin Meng 96038ad43e4SBin Meng /* 96138ad43e4SBin Meng * This function will return the internal Vref setting for the given 96238ad43e4SBin Meng * channel, byte_lane. 96338ad43e4SBin Meng */ 96438ad43e4SBin Meng uint32_t get_vref(uint8_t channel, uint8_t byte_lane) 96538ad43e4SBin Meng { 96638ad43e4SBin Meng uint8_t j; 96738ad43e4SBin Meng uint32_t ret_val = sizeof(vref_codes) / 2; 968*312cc39eSBin Meng uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL; 96938ad43e4SBin Meng uint32_t temp; 97038ad43e4SBin Meng 97138ad43e4SBin Meng ENTERFN(); 97238ad43e4SBin Meng 973*312cc39eSBin Meng temp = msg_port_alt_read(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET + 974*312cc39eSBin Meng (byte_lane >> 1) * DDRIODQ_BL_OFFSET); 97538ad43e4SBin Meng temp >>= 2; 976*312cc39eSBin Meng temp &= 0x3f; 97738ad43e4SBin Meng 97838ad43e4SBin Meng for (j = 0; j < sizeof(vref_codes); j++) { 97938ad43e4SBin Meng if (vref_codes[j] == temp) { 98038ad43e4SBin Meng ret_val = j; 98138ad43e4SBin Meng break; 98238ad43e4SBin Meng } 98338ad43e4SBin Meng } 98438ad43e4SBin Meng 98538ad43e4SBin Meng LEAVEFN(); 98638ad43e4SBin Meng 98738ad43e4SBin Meng return ret_val; 98838ad43e4SBin Meng } 98938ad43e4SBin Meng 99038ad43e4SBin Meng /* 99138ad43e4SBin Meng * This function will return a 32-bit address in the desired 99238ad43e4SBin Meng * channel and rank. 99338ad43e4SBin Meng */ 99438ad43e4SBin Meng uint32_t get_addr(uint8_t channel, uint8_t rank) 99538ad43e4SBin Meng { 996*312cc39eSBin Meng uint32_t offset = 32 * 1024 * 1024; /* 32MB */ 99738ad43e4SBin Meng 99838ad43e4SBin Meng /* Begin product specific code */ 99938ad43e4SBin Meng if (channel > 0) { 100038ad43e4SBin Meng DPF(D_ERROR, "ILLEGAL CHANNEL\n"); 100138ad43e4SBin Meng DEAD_LOOP(); 100238ad43e4SBin Meng } 100338ad43e4SBin Meng 100438ad43e4SBin Meng if (rank > 1) { 100538ad43e4SBin Meng DPF(D_ERROR, "ILLEGAL RANK\n"); 100638ad43e4SBin Meng DEAD_LOOP(); 100738ad43e4SBin Meng } 100838ad43e4SBin Meng 100938ad43e4SBin Meng /* use 256MB lowest density as per DRP == 0x0003 */ 101038ad43e4SBin Meng offset += rank * (256 * 1024 * 1024); 101138ad43e4SBin Meng 101238ad43e4SBin Meng return offset; 101338ad43e4SBin Meng } 101438ad43e4SBin Meng 101538ad43e4SBin Meng /* 101638ad43e4SBin Meng * This function will sample the DQTRAINSTS registers in the given 101738ad43e4SBin Meng * channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'. 101838ad43e4SBin Meng * 101938ad43e4SBin Meng * It will return an encoded 32-bit date in which each bit corresponds to 102038ad43e4SBin Meng * the sampled value on the byte lane. 102138ad43e4SBin Meng */ 102238ad43e4SBin Meng uint32_t sample_dqs(struct mrc_params *mrc_params, uint8_t channel, 102338ad43e4SBin Meng uint8_t rank, bool rcvn) 102438ad43e4SBin Meng { 102538ad43e4SBin Meng uint8_t j; /* just a counter */ 102638ad43e4SBin Meng uint8_t bl; /* which BL in the module (always 2 per module) */ 102738ad43e4SBin Meng uint8_t bl_grp; /* which BL module */ 102838ad43e4SBin Meng /* byte lane divisor */ 102938ad43e4SBin Meng uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; 103038ad43e4SBin Meng uint32_t msk[2]; /* BLx in module */ 103138ad43e4SBin Meng /* DQTRAINSTS register contents for each sample */ 103238ad43e4SBin Meng uint32_t sampled_val[SAMPLE_SIZE]; 103338ad43e4SBin Meng uint32_t num_0s; /* tracks the number of '0' samples */ 103438ad43e4SBin Meng uint32_t num_1s; /* tracks the number of '1' samples */ 103538ad43e4SBin Meng uint32_t ret_val = 0x00; /* assume all '0' samples */ 103638ad43e4SBin Meng uint32_t address = get_addr(channel, rank); 103738ad43e4SBin Meng 103838ad43e4SBin Meng /* initialise msk[] */ 1039*312cc39eSBin Meng msk[0] = rcvn ? (1 << 1) : (1 << 9); /* BL0 */ 1040*312cc39eSBin Meng msk[1] = rcvn ? (1 << 0) : (1 << 8); /* BL1 */ 104138ad43e4SBin Meng 104238ad43e4SBin Meng /* cycle through each byte lane group */ 104338ad43e4SBin Meng for (bl_grp = 0; bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; bl_grp++) { 104438ad43e4SBin Meng /* take SAMPLE_SIZE samples */ 104538ad43e4SBin Meng for (j = 0; j < SAMPLE_SIZE; j++) { 104638ad43e4SBin Meng hte_mem_op(address, mrc_params->first_run, 104738ad43e4SBin Meng rcvn ? 0 : 1); 104838ad43e4SBin Meng mrc_params->first_run = 0; 104938ad43e4SBin Meng 105038ad43e4SBin Meng /* 105138ad43e4SBin Meng * record the contents of the proper 105238ad43e4SBin Meng * DQTRAINSTS register 105338ad43e4SBin Meng */ 105438ad43e4SBin Meng sampled_val[j] = msg_port_alt_read(DDRPHY, 1055*312cc39eSBin Meng DQTRAINSTS + 1056*312cc39eSBin Meng bl_grp * DDRIODQ_BL_OFFSET + 1057*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET); 105838ad43e4SBin Meng } 105938ad43e4SBin Meng 106038ad43e4SBin Meng /* 106138ad43e4SBin Meng * look for a majority value (SAMPLE_SIZE / 2) + 1 106238ad43e4SBin Meng * on the byte lane and set that value in the corresponding 106338ad43e4SBin Meng * ret_val bit 106438ad43e4SBin Meng */ 106538ad43e4SBin Meng for (bl = 0; bl < 2; bl++) { 106638ad43e4SBin Meng num_0s = 0x00; /* reset '0' tracker for byte lane */ 106738ad43e4SBin Meng num_1s = 0x00; /* reset '1' tracker for byte lane */ 106838ad43e4SBin Meng for (j = 0; j < SAMPLE_SIZE; j++) { 106938ad43e4SBin Meng if (sampled_val[j] & msk[bl]) 107038ad43e4SBin Meng num_1s++; 107138ad43e4SBin Meng else 107238ad43e4SBin Meng num_0s++; 107338ad43e4SBin Meng } 107438ad43e4SBin Meng if (num_1s > num_0s) 1075*312cc39eSBin Meng ret_val |= (1 << (bl + bl_grp * 2)); 107638ad43e4SBin Meng } 107738ad43e4SBin Meng } 107838ad43e4SBin Meng 107938ad43e4SBin Meng /* 108038ad43e4SBin Meng * "ret_val.0" contains the status of BL0 108138ad43e4SBin Meng * "ret_val.1" contains the status of BL1 108238ad43e4SBin Meng * "ret_val.2" contains the status of BL2 108338ad43e4SBin Meng * etc. 108438ad43e4SBin Meng */ 108538ad43e4SBin Meng return ret_val; 108638ad43e4SBin Meng } 108738ad43e4SBin Meng 108838ad43e4SBin Meng /* This function will find the rising edge transition on RCVN or WDQS */ 108938ad43e4SBin Meng void find_rising_edge(struct mrc_params *mrc_params, uint32_t delay[], 109038ad43e4SBin Meng uint8_t channel, uint8_t rank, bool rcvn) 109138ad43e4SBin Meng { 109238ad43e4SBin Meng bool all_edges_found; /* determines stop condition */ 109338ad43e4SBin Meng bool direction[NUM_BYTE_LANES]; /* direction indicator */ 109438ad43e4SBin Meng uint8_t sample; /* sample counter */ 109538ad43e4SBin Meng uint8_t bl; /* byte lane counter */ 109638ad43e4SBin Meng /* byte lane divisor */ 109738ad43e4SBin Meng uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; 109838ad43e4SBin Meng uint32_t sample_result[SAMPLE_CNT]; /* results of sample_dqs() */ 109938ad43e4SBin Meng uint32_t temp; 110038ad43e4SBin Meng uint32_t transition_pattern; 110138ad43e4SBin Meng 110238ad43e4SBin Meng ENTERFN(); 110338ad43e4SBin Meng 110438ad43e4SBin Meng /* select hte and request initial configuration */ 110538ad43e4SBin Meng select_hte(); 110638ad43e4SBin Meng mrc_params->first_run = 1; 110738ad43e4SBin Meng 110838ad43e4SBin Meng /* Take 3 sample points (T1,T2,T3) to obtain a transition pattern */ 110938ad43e4SBin Meng for (sample = 0; sample < SAMPLE_CNT; sample++) { 111038ad43e4SBin Meng /* program the desired delays for sample */ 111138ad43e4SBin Meng for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { 111238ad43e4SBin Meng /* increase sample delay by 26 PI (0.2 CLK) */ 111338ad43e4SBin Meng if (rcvn) { 111438ad43e4SBin Meng set_rcvn(channel, rank, bl, 1115*312cc39eSBin Meng delay[bl] + sample * SAMPLE_DLY); 111638ad43e4SBin Meng } else { 111738ad43e4SBin Meng set_wdqs(channel, rank, bl, 1118*312cc39eSBin Meng delay[bl] + sample * SAMPLE_DLY); 111938ad43e4SBin Meng } 112038ad43e4SBin Meng } 112138ad43e4SBin Meng 112238ad43e4SBin Meng /* take samples (Tsample_i) */ 112338ad43e4SBin Meng sample_result[sample] = sample_dqs(mrc_params, 112438ad43e4SBin Meng channel, rank, rcvn); 112538ad43e4SBin Meng 112638ad43e4SBin Meng DPF(D_TRN, 112738ad43e4SBin Meng "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n", 1128*312cc39eSBin Meng rcvn ? "RCVN" : "WDQS", channel, rank, sample, 112938ad43e4SBin Meng sample * SAMPLE_DLY, sample_result[sample]); 113038ad43e4SBin Meng } 113138ad43e4SBin Meng 113238ad43e4SBin Meng /* 113338ad43e4SBin Meng * This pattern will help determine where we landed and ultimately 113438ad43e4SBin Meng * how to place RCVEN/WDQS. 113538ad43e4SBin Meng */ 1136*312cc39eSBin Meng for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { 113738ad43e4SBin Meng /* build transition_pattern (MSB is 1st sample) */ 113838ad43e4SBin Meng transition_pattern = 0; 113938ad43e4SBin Meng for (sample = 0; sample < SAMPLE_CNT; sample++) { 114038ad43e4SBin Meng transition_pattern |= 114138ad43e4SBin Meng ((sample_result[sample] & (1 << bl)) >> bl) << 114238ad43e4SBin Meng (SAMPLE_CNT - 1 - sample); 114338ad43e4SBin Meng } 114438ad43e4SBin Meng 114538ad43e4SBin Meng DPF(D_TRN, "=== transition pattern %d\n", transition_pattern); 114638ad43e4SBin Meng 114738ad43e4SBin Meng /* 114838ad43e4SBin Meng * set up to look for rising edge based on 114938ad43e4SBin Meng * transition_pattern 115038ad43e4SBin Meng */ 115138ad43e4SBin Meng switch (transition_pattern) { 115238ad43e4SBin Meng case 0: /* sampled 0->0->0 */ 115338ad43e4SBin Meng /* move forward from T3 looking for 0->1 */ 115438ad43e4SBin Meng delay[bl] += 2 * SAMPLE_DLY; 115538ad43e4SBin Meng direction[bl] = FORWARD; 115638ad43e4SBin Meng break; 115738ad43e4SBin Meng case 1: /* sampled 0->0->1 */ 115838ad43e4SBin Meng case 5: /* sampled 1->0->1 (bad duty cycle) *HSD#237503* */ 115938ad43e4SBin Meng /* move forward from T2 looking for 0->1 */ 116038ad43e4SBin Meng delay[bl] += 1 * SAMPLE_DLY; 116138ad43e4SBin Meng direction[bl] = FORWARD; 116238ad43e4SBin Meng break; 116338ad43e4SBin Meng case 2: /* sampled 0->1->0 (bad duty cycle) *HSD#237503* */ 116438ad43e4SBin Meng case 3: /* sampled 0->1->1 */ 116538ad43e4SBin Meng /* move forward from T1 looking for 0->1 */ 116638ad43e4SBin Meng delay[bl] += 0 * SAMPLE_DLY; 116738ad43e4SBin Meng direction[bl] = FORWARD; 116838ad43e4SBin Meng break; 116938ad43e4SBin Meng case 4: /* sampled 1->0->0 (assumes BL8, HSD#234975) */ 117038ad43e4SBin Meng /* move forward from T3 looking for 0->1 */ 117138ad43e4SBin Meng delay[bl] += 2 * SAMPLE_DLY; 117238ad43e4SBin Meng direction[bl] = FORWARD; 117338ad43e4SBin Meng break; 117438ad43e4SBin Meng case 6: /* sampled 1->1->0 */ 117538ad43e4SBin Meng case 7: /* sampled 1->1->1 */ 117638ad43e4SBin Meng /* move backward from T1 looking for 1->0 */ 117738ad43e4SBin Meng delay[bl] += 0 * SAMPLE_DLY; 117838ad43e4SBin Meng direction[bl] = BACKWARD; 117938ad43e4SBin Meng break; 118038ad43e4SBin Meng default: 118138ad43e4SBin Meng mrc_post_code(0xee, 0xee); 118238ad43e4SBin Meng break; 118338ad43e4SBin Meng } 118438ad43e4SBin Meng 118538ad43e4SBin Meng /* program delays */ 118638ad43e4SBin Meng if (rcvn) 118738ad43e4SBin Meng set_rcvn(channel, rank, bl, delay[bl]); 118838ad43e4SBin Meng else 118938ad43e4SBin Meng set_wdqs(channel, rank, bl, delay[bl]); 119038ad43e4SBin Meng } 119138ad43e4SBin Meng 119238ad43e4SBin Meng /* 119338ad43e4SBin Meng * Based on the observed transition pattern on the byte lane, 119438ad43e4SBin Meng * begin looking for a rising edge with single PI granularity. 119538ad43e4SBin Meng */ 119638ad43e4SBin Meng do { 119738ad43e4SBin Meng all_edges_found = true; /* assume all byte lanes passed */ 119838ad43e4SBin Meng /* take a sample */ 119938ad43e4SBin Meng temp = sample_dqs(mrc_params, channel, rank, rcvn); 120038ad43e4SBin Meng /* check all each byte lane for proper edge */ 1201*312cc39eSBin Meng for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { 120238ad43e4SBin Meng if (temp & (1 << bl)) { 120338ad43e4SBin Meng /* sampled "1" */ 120438ad43e4SBin Meng if (direction[bl] == BACKWARD) { 120538ad43e4SBin Meng /* 120638ad43e4SBin Meng * keep looking for edge 120738ad43e4SBin Meng * on this byte lane 120838ad43e4SBin Meng */ 120938ad43e4SBin Meng all_edges_found = false; 121038ad43e4SBin Meng delay[bl] -= 1; 121138ad43e4SBin Meng if (rcvn) { 121238ad43e4SBin Meng set_rcvn(channel, rank, 121338ad43e4SBin Meng bl, delay[bl]); 121438ad43e4SBin Meng } else { 121538ad43e4SBin Meng set_wdqs(channel, rank, 121638ad43e4SBin Meng bl, delay[bl]); 121738ad43e4SBin Meng } 121838ad43e4SBin Meng } 121938ad43e4SBin Meng } else { 122038ad43e4SBin Meng /* sampled "0" */ 122138ad43e4SBin Meng if (direction[bl] == FORWARD) { 122238ad43e4SBin Meng /* 122338ad43e4SBin Meng * keep looking for edge 122438ad43e4SBin Meng * on this byte lane 122538ad43e4SBin Meng */ 122638ad43e4SBin Meng all_edges_found = false; 122738ad43e4SBin Meng delay[bl] += 1; 122838ad43e4SBin Meng if (rcvn) { 122938ad43e4SBin Meng set_rcvn(channel, rank, 123038ad43e4SBin Meng bl, delay[bl]); 123138ad43e4SBin Meng } else { 123238ad43e4SBin Meng set_wdqs(channel, rank, 123338ad43e4SBin Meng bl, delay[bl]); 123438ad43e4SBin Meng } 123538ad43e4SBin Meng } 123638ad43e4SBin Meng } 123738ad43e4SBin Meng } 123838ad43e4SBin Meng } while (!all_edges_found); 123938ad43e4SBin Meng 124038ad43e4SBin Meng /* restore DDR idle state */ 124138ad43e4SBin Meng dram_init_command(DCMD_PREA(rank)); 124238ad43e4SBin Meng 124338ad43e4SBin Meng DPF(D_TRN, "Delay %03X %03X %03X %03X\n", 124438ad43e4SBin Meng delay[0], delay[1], delay[2], delay[3]); 124538ad43e4SBin Meng 124638ad43e4SBin Meng LEAVEFN(); 124738ad43e4SBin Meng } 124838ad43e4SBin Meng 124938ad43e4SBin Meng /* 125038ad43e4SBin Meng * This function will return a 32 bit mask that will be used to 125138ad43e4SBin Meng * check for byte lane failures. 125238ad43e4SBin Meng */ 125338ad43e4SBin Meng uint32_t byte_lane_mask(struct mrc_params *mrc_params) 125438ad43e4SBin Meng { 125538ad43e4SBin Meng uint32_t j; 125638ad43e4SBin Meng uint32_t ret_val = 0x00; 125738ad43e4SBin Meng 125838ad43e4SBin Meng /* 125938ad43e4SBin Meng * set ret_val based on NUM_BYTE_LANES such that you will check 126038ad43e4SBin Meng * only BL0 in result 126138ad43e4SBin Meng * 126238ad43e4SBin Meng * (each bit in result represents a byte lane) 126338ad43e4SBin Meng */ 126438ad43e4SBin Meng for (j = 0; j < MAX_BYTE_LANES; j += NUM_BYTE_LANES) 126538ad43e4SBin Meng ret_val |= (1 << ((j / NUM_BYTE_LANES) * NUM_BYTE_LANES)); 126638ad43e4SBin Meng 126738ad43e4SBin Meng /* 126838ad43e4SBin Meng * HSD#235037 126938ad43e4SBin Meng * need to adjust the mask for 16-bit mode 127038ad43e4SBin Meng */ 127138ad43e4SBin Meng if (mrc_params->channel_width == X16) 127238ad43e4SBin Meng ret_val |= (ret_val << 2); 127338ad43e4SBin Meng 127438ad43e4SBin Meng return ret_val; 127538ad43e4SBin Meng } 127638ad43e4SBin Meng 127738ad43e4SBin Meng /* 127838ad43e4SBin Meng * Check memory executing simple write/read/verify at the specified address. 127938ad43e4SBin Meng * 128038ad43e4SBin Meng * Bits in the result indicate failure on specific byte lane. 128138ad43e4SBin Meng */ 128238ad43e4SBin Meng uint32_t check_rw_coarse(struct mrc_params *mrc_params, uint32_t address) 128338ad43e4SBin Meng { 128438ad43e4SBin Meng uint32_t result = 0; 128538ad43e4SBin Meng uint8_t first_run = 0; 128638ad43e4SBin Meng 128738ad43e4SBin Meng if (mrc_params->hte_setup) { 128838ad43e4SBin Meng mrc_params->hte_setup = 0; 128938ad43e4SBin Meng first_run = 1; 129038ad43e4SBin Meng select_hte(); 129138ad43e4SBin Meng } 129238ad43e4SBin Meng 129338ad43e4SBin Meng result = hte_basic_write_read(mrc_params, address, first_run, 129438ad43e4SBin Meng WRITE_TRAIN); 129538ad43e4SBin Meng 129638ad43e4SBin Meng DPF(D_TRN, "check_rw_coarse result is %x\n", result); 129738ad43e4SBin Meng 129838ad43e4SBin Meng return result; 129938ad43e4SBin Meng } 130038ad43e4SBin Meng 130138ad43e4SBin Meng /* 130238ad43e4SBin Meng * Check memory executing write/read/verify of many data patterns 130338ad43e4SBin Meng * at the specified address. Bits in the result indicate failure 130438ad43e4SBin Meng * on specific byte lane. 130538ad43e4SBin Meng */ 130638ad43e4SBin Meng uint32_t check_bls_ex(struct mrc_params *mrc_params, uint32_t address) 130738ad43e4SBin Meng { 130838ad43e4SBin Meng uint32_t result; 130938ad43e4SBin Meng uint8_t first_run = 0; 131038ad43e4SBin Meng 131138ad43e4SBin Meng if (mrc_params->hte_setup) { 131238ad43e4SBin Meng mrc_params->hte_setup = 0; 131338ad43e4SBin Meng first_run = 1; 131438ad43e4SBin Meng select_hte(); 131538ad43e4SBin Meng } 131638ad43e4SBin Meng 131738ad43e4SBin Meng result = hte_write_stress_bit_lanes(mrc_params, address, first_run); 131838ad43e4SBin Meng 131938ad43e4SBin Meng DPF(D_TRN, "check_bls_ex result is %x\n", result); 132038ad43e4SBin Meng 132138ad43e4SBin Meng return result; 132238ad43e4SBin Meng } 132338ad43e4SBin Meng 132438ad43e4SBin Meng /* 132538ad43e4SBin Meng * 32-bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1 132638ad43e4SBin Meng * 132738ad43e4SBin Meng * The function takes pointer to previous 32 bit value and 132838ad43e4SBin Meng * modifies it to next value. 132938ad43e4SBin Meng */ 133038ad43e4SBin Meng void lfsr32(uint32_t *lfsr_ptr) 133138ad43e4SBin Meng { 133238ad43e4SBin Meng uint32_t bit; 133338ad43e4SBin Meng uint32_t lfsr; 133438ad43e4SBin Meng int i; 133538ad43e4SBin Meng 133638ad43e4SBin Meng lfsr = *lfsr_ptr; 133738ad43e4SBin Meng 133838ad43e4SBin Meng for (i = 0; i < 32; i++) { 1339*312cc39eSBin Meng bit = 1 ^ (lfsr & 1); 1340*312cc39eSBin Meng bit = bit ^ ((lfsr & 2) >> 1); 1341*312cc39eSBin Meng bit = bit ^ ((lfsr & 4) >> 2); 1342*312cc39eSBin Meng bit = bit ^ ((lfsr & 0x400000) >> 22); 134338ad43e4SBin Meng 134438ad43e4SBin Meng lfsr = ((lfsr >> 1) | (bit << 31)); 134538ad43e4SBin Meng } 134638ad43e4SBin Meng 134738ad43e4SBin Meng *lfsr_ptr = lfsr; 134838ad43e4SBin Meng } 134938ad43e4SBin Meng 135038ad43e4SBin Meng /* Clear the pointers in a given byte lane in a given channel */ 135138ad43e4SBin Meng void clear_pointers(void) 135238ad43e4SBin Meng { 135338ad43e4SBin Meng uint8_t channel; 135438ad43e4SBin Meng uint8_t bl; 135538ad43e4SBin Meng 135638ad43e4SBin Meng ENTERFN(); 135738ad43e4SBin Meng 135838ad43e4SBin Meng for (channel = 0; channel < NUM_CHANNELS; channel++) { 135938ad43e4SBin Meng for (bl = 0; bl < NUM_BYTE_LANES; bl++) { 136038ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, 1361*312cc39eSBin Meng B01PTRCTL1 + 1362*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET + 1363*312cc39eSBin Meng (bl >> 1) * DDRIODQ_BL_OFFSET, 1364*312cc39eSBin Meng ~(1 << 8), (1 << 8)); 136538ad43e4SBin Meng 136638ad43e4SBin Meng mrc_alt_write_mask(DDRPHY, 1367*312cc39eSBin Meng B01PTRCTL1 + 1368*312cc39eSBin Meng channel * DDRIODQ_CH_OFFSET + 1369*312cc39eSBin Meng (bl >> 1) * DDRIODQ_BL_OFFSET, 1370*312cc39eSBin Meng (1 << 8), (1 << 8)); 137138ad43e4SBin Meng } 137238ad43e4SBin Meng } 137338ad43e4SBin Meng 137438ad43e4SBin Meng LEAVEFN(); 137538ad43e4SBin Meng } 137638ad43e4SBin Meng 137738ad43e4SBin Meng static void print_timings_internal(uint8_t algo, uint8_t channel, uint8_t rank, 137838ad43e4SBin Meng uint8_t bl_divisor) 137938ad43e4SBin Meng { 138038ad43e4SBin Meng uint8_t bl; 138138ad43e4SBin Meng 138238ad43e4SBin Meng switch (algo) { 138338ad43e4SBin Meng case RCVN: 138438ad43e4SBin Meng DPF(D_INFO, "\nRCVN[%02d:%02d]", channel, rank); 138538ad43e4SBin Meng break; 138638ad43e4SBin Meng case WDQS: 138738ad43e4SBin Meng DPF(D_INFO, "\nWDQS[%02d:%02d]", channel, rank); 138838ad43e4SBin Meng break; 138938ad43e4SBin Meng case WDQX: 139038ad43e4SBin Meng DPF(D_INFO, "\nWDQx[%02d:%02d]", channel, rank); 139138ad43e4SBin Meng break; 139238ad43e4SBin Meng case RDQS: 139338ad43e4SBin Meng DPF(D_INFO, "\nRDQS[%02d:%02d]", channel, rank); 139438ad43e4SBin Meng break; 139538ad43e4SBin Meng case VREF: 139638ad43e4SBin Meng DPF(D_INFO, "\nVREF[%02d:%02d]", channel, rank); 139738ad43e4SBin Meng break; 139838ad43e4SBin Meng case WCMD: 139938ad43e4SBin Meng DPF(D_INFO, "\nWCMD[%02d:%02d]", channel, rank); 140038ad43e4SBin Meng break; 140138ad43e4SBin Meng case WCTL: 140238ad43e4SBin Meng DPF(D_INFO, "\nWCTL[%02d:%02d]", channel, rank); 140338ad43e4SBin Meng break; 140438ad43e4SBin Meng case WCLK: 140538ad43e4SBin Meng DPF(D_INFO, "\nWCLK[%02d:%02d]", channel, rank); 140638ad43e4SBin Meng break; 140738ad43e4SBin Meng default: 140838ad43e4SBin Meng break; 140938ad43e4SBin Meng } 141038ad43e4SBin Meng 1411*312cc39eSBin Meng for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { 141238ad43e4SBin Meng switch (algo) { 141338ad43e4SBin Meng case RCVN: 141438ad43e4SBin Meng DPF(D_INFO, " %03d", get_rcvn(channel, rank, bl)); 141538ad43e4SBin Meng break; 141638ad43e4SBin Meng case WDQS: 141738ad43e4SBin Meng DPF(D_INFO, " %03d", get_wdqs(channel, rank, bl)); 141838ad43e4SBin Meng break; 141938ad43e4SBin Meng case WDQX: 142038ad43e4SBin Meng DPF(D_INFO, " %03d", get_wdq(channel, rank, bl)); 142138ad43e4SBin Meng break; 142238ad43e4SBin Meng case RDQS: 142338ad43e4SBin Meng DPF(D_INFO, " %03d", get_rdqs(channel, rank, bl)); 142438ad43e4SBin Meng break; 142538ad43e4SBin Meng case VREF: 142638ad43e4SBin Meng DPF(D_INFO, " %03d", get_vref(channel, bl)); 142738ad43e4SBin Meng break; 142838ad43e4SBin Meng case WCMD: 142938ad43e4SBin Meng DPF(D_INFO, " %03d", get_wcmd(channel)); 143038ad43e4SBin Meng break; 143138ad43e4SBin Meng case WCTL: 143238ad43e4SBin Meng DPF(D_INFO, " %03d", get_wctl(channel, rank)); 143338ad43e4SBin Meng break; 143438ad43e4SBin Meng case WCLK: 143538ad43e4SBin Meng DPF(D_INFO, " %03d", get_wclk(channel, rank)); 143638ad43e4SBin Meng break; 143738ad43e4SBin Meng default: 143838ad43e4SBin Meng break; 143938ad43e4SBin Meng } 144038ad43e4SBin Meng } 144138ad43e4SBin Meng } 144238ad43e4SBin Meng 144338ad43e4SBin Meng void print_timings(struct mrc_params *mrc_params) 144438ad43e4SBin Meng { 144538ad43e4SBin Meng uint8_t algo; 144638ad43e4SBin Meng uint8_t channel; 144738ad43e4SBin Meng uint8_t rank; 144838ad43e4SBin Meng uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; 144938ad43e4SBin Meng 145038ad43e4SBin Meng DPF(D_INFO, "\n---------------------------"); 145138ad43e4SBin Meng DPF(D_INFO, "\nALGO[CH:RK] BL0 BL1 BL2 BL3"); 145238ad43e4SBin Meng DPF(D_INFO, "\n==========================="); 145338ad43e4SBin Meng 145438ad43e4SBin Meng for (algo = 0; algo < MAX_ALGOS; algo++) { 145538ad43e4SBin Meng for (channel = 0; channel < NUM_CHANNELS; channel++) { 145638ad43e4SBin Meng if (mrc_params->channel_enables & (1 << channel)) { 145738ad43e4SBin Meng for (rank = 0; rank < NUM_RANKS; rank++) { 145838ad43e4SBin Meng if (mrc_params->rank_enables & 145938ad43e4SBin Meng (1 << rank)) { 146038ad43e4SBin Meng print_timings_internal(algo, 146138ad43e4SBin Meng channel, rank, 146238ad43e4SBin Meng bl_divisor); 146338ad43e4SBin Meng } 146438ad43e4SBin Meng } 146538ad43e4SBin Meng } 146638ad43e4SBin Meng } 146738ad43e4SBin Meng } 146838ad43e4SBin Meng 146938ad43e4SBin Meng DPF(D_INFO, "\n---------------------------"); 147038ad43e4SBin Meng DPF(D_INFO, "\n"); 147138ad43e4SBin Meng } 1472