xref: /rk3399_rockchip-uboot/arch/x86/cpu/quark/mrc_util.c (revision 5a257df702e5702f46fca548d0899c65e90df947)
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>
15*5a257df7SBin Meng #include <asm/arch/quark.h>
1638ad43e4SBin Meng #include "mrc_util.h"
1738ad43e4SBin Meng #include "hte.h"
1838ad43e4SBin Meng #include "smc.h"
1938ad43e4SBin Meng 
2038ad43e4SBin Meng static const uint8_t vref_codes[64] = {
2138ad43e4SBin Meng 	/* lowest to highest */
22312cc39eSBin Meng 	0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
2338ad43e4SBin Meng 	0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
24312cc39eSBin Meng 	0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
2538ad43e4SBin Meng 	0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
2638ad43e4SBin Meng 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
27312cc39eSBin Meng 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
2838ad43e4SBin Meng 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
29312cc39eSBin Meng 	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
3038ad43e4SBin Meng };
3138ad43e4SBin Meng 
mrc_write_mask(u32 unit,u32 addr,u32 data,u32 mask)3238ad43e4SBin Meng void mrc_write_mask(u32 unit, u32 addr, u32 data, u32 mask)
3338ad43e4SBin Meng {
3438ad43e4SBin Meng 	msg_port_write(unit, addr,
3538ad43e4SBin Meng 		       (msg_port_read(unit, addr) & ~(mask)) |
3638ad43e4SBin Meng 		       ((data) & (mask)));
3738ad43e4SBin Meng }
3838ad43e4SBin Meng 
mrc_alt_write_mask(u32 unit,u32 addr,u32 data,u32 mask)3938ad43e4SBin Meng void mrc_alt_write_mask(u32 unit, u32 addr, u32 data, u32 mask)
4038ad43e4SBin Meng {
4138ad43e4SBin Meng 	msg_port_alt_write(unit, addr,
4238ad43e4SBin Meng 			   (msg_port_alt_read(unit, addr) & ~(mask)) |
4338ad43e4SBin Meng 			   ((data) & (mask)));
4438ad43e4SBin Meng }
4538ad43e4SBin Meng 
mrc_post_code(uint8_t major,uint8_t minor)4638ad43e4SBin Meng void mrc_post_code(uint8_t major, uint8_t minor)
4738ad43e4SBin Meng {
4838ad43e4SBin Meng 	/* send message to UART */
4938ad43e4SBin Meng 	DPF(D_INFO, "POST: 0x%01x%02x\n", major, minor);
5038ad43e4SBin Meng 
5138ad43e4SBin Meng 	/* error check */
5238ad43e4SBin Meng 	if (major == 0xee)
5338ad43e4SBin Meng 		hang();
5438ad43e4SBin Meng }
5538ad43e4SBin Meng 
5638ad43e4SBin Meng /* Delay number of nanoseconds */
delay_n(uint32_t ns)5738ad43e4SBin Meng void delay_n(uint32_t ns)
5838ad43e4SBin Meng {
5938ad43e4SBin Meng 	/* 1000 MHz clock has 1ns period --> no conversion required */
6038ad43e4SBin Meng 	uint64_t final_tsc = rdtsc();
6138ad43e4SBin Meng 
6238ad43e4SBin Meng 	final_tsc += ((get_tbclk_mhz() * ns) / 1000);
6338ad43e4SBin Meng 
6438ad43e4SBin Meng 	while (rdtsc() < final_tsc)
6538ad43e4SBin Meng 		;
6638ad43e4SBin Meng }
6738ad43e4SBin Meng 
6838ad43e4SBin Meng /* Delay number of microseconds */
delay_u(uint32_t ms)6938ad43e4SBin Meng void delay_u(uint32_t ms)
7038ad43e4SBin Meng {
7138ad43e4SBin Meng 	/* 64-bit math is not an option, just use loops */
7238ad43e4SBin Meng 	while (ms--)
7338ad43e4SBin Meng 		delay_n(1000);
7438ad43e4SBin Meng }
7538ad43e4SBin Meng 
7638ad43e4SBin Meng /* Select Memory Manager as the source for PRI interface */
select_mem_mgr(void)7738ad43e4SBin Meng void select_mem_mgr(void)
7838ad43e4SBin Meng {
7938ad43e4SBin Meng 	u32 dco;
8038ad43e4SBin Meng 
8138ad43e4SBin Meng 	ENTERFN();
8238ad43e4SBin Meng 
8338ad43e4SBin Meng 	dco = msg_port_read(MEM_CTLR, DCO);
84312cc39eSBin Meng 	dco &= ~DCO_PMICTL;
8538ad43e4SBin Meng 	msg_port_write(MEM_CTLR, DCO, dco);
8638ad43e4SBin Meng 
8738ad43e4SBin Meng 	LEAVEFN();
8838ad43e4SBin Meng }
8938ad43e4SBin Meng 
9038ad43e4SBin Meng /* Select HTE as the source for PRI interface */
select_hte(void)9138ad43e4SBin Meng void select_hte(void)
9238ad43e4SBin Meng {
9338ad43e4SBin Meng 	u32 dco;
9438ad43e4SBin Meng 
9538ad43e4SBin Meng 	ENTERFN();
9638ad43e4SBin Meng 
9738ad43e4SBin Meng 	dco = msg_port_read(MEM_CTLR, DCO);
98312cc39eSBin Meng 	dco |= DCO_PMICTL;
9938ad43e4SBin Meng 	msg_port_write(MEM_CTLR, DCO, dco);
10038ad43e4SBin Meng 
10138ad43e4SBin Meng 	LEAVEFN();
10238ad43e4SBin Meng }
10338ad43e4SBin Meng 
10438ad43e4SBin Meng /*
10538ad43e4SBin Meng  * Send DRAM command
10638ad43e4SBin Meng  * data should be formated using DCMD_Xxxx macro or emrsXCommand structure
10738ad43e4SBin Meng  */
dram_init_command(uint32_t data)10838ad43e4SBin Meng void dram_init_command(uint32_t data)
10938ad43e4SBin Meng {
110*5a257df7SBin Meng 	qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, data);
111*5a257df7SBin Meng 	qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG, 0);
11238ad43e4SBin Meng 	msg_port_setup(MSG_OP_DRAM_INIT, MEM_CTLR, 0);
11338ad43e4SBin Meng 
11438ad43e4SBin Meng 	DPF(D_REGWR, "WR32 %03X %08X %08X\n", MEM_CTLR, 0, data);
11538ad43e4SBin Meng }
11638ad43e4SBin Meng 
11738ad43e4SBin Meng /* Send DRAM wake command using special MCU side-band WAKE opcode */
dram_wake_command(void)11838ad43e4SBin Meng void dram_wake_command(void)
11938ad43e4SBin Meng {
12038ad43e4SBin Meng 	ENTERFN();
12138ad43e4SBin Meng 
12238ad43e4SBin Meng 	msg_port_setup(MSG_OP_DRAM_WAKE, MEM_CTLR, 0);
12338ad43e4SBin Meng 
12438ad43e4SBin Meng 	LEAVEFN();
12538ad43e4SBin Meng }
12638ad43e4SBin Meng 
training_message(uint8_t channel,uint8_t rank,uint8_t byte_lane)12738ad43e4SBin Meng void training_message(uint8_t channel, uint8_t rank, uint8_t byte_lane)
12838ad43e4SBin Meng {
12938ad43e4SBin Meng 	/* send message to UART */
13038ad43e4SBin Meng 	DPF(D_INFO, "CH%01X RK%01X BL%01X\n", channel, rank, byte_lane);
13138ad43e4SBin Meng }
13238ad43e4SBin Meng 
13338ad43e4SBin Meng /*
13438ad43e4SBin Meng  * This function will program the RCVEN delays
13538ad43e4SBin Meng  *
13638ad43e4SBin Meng  * (currently doesn't comprehend rank)
13738ad43e4SBin Meng  */
set_rcvn(uint8_t channel,uint8_t rank,uint8_t byte_lane,uint32_t pi_count)13838ad43e4SBin Meng void set_rcvn(uint8_t channel, uint8_t rank,
13938ad43e4SBin Meng 	      uint8_t byte_lane, uint32_t pi_count)
14038ad43e4SBin Meng {
14138ad43e4SBin Meng 	uint32_t reg;
14238ad43e4SBin Meng 	uint32_t msk;
14338ad43e4SBin Meng 	uint32_t temp;
14438ad43e4SBin Meng 
14538ad43e4SBin Meng 	ENTERFN();
14638ad43e4SBin Meng 
14738ad43e4SBin Meng 	DPF(D_TRN, "Rcvn ch%d rnk%d ln%d : pi=%03X\n",
14838ad43e4SBin Meng 	    channel, rank, byte_lane, pi_count);
14938ad43e4SBin Meng 
15038ad43e4SBin Meng 	/*
15138ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
15238ad43e4SBin Meng 	 * BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
15338ad43e4SBin Meng 	 * BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
15438ad43e4SBin Meng 	 */
155312cc39eSBin Meng 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
156312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
157312cc39eSBin Meng 	msk = (byte_lane & 1) ? 0xf00000 : 0xf00;
158312cc39eSBin Meng 	temp = (byte_lane & 1) ? (pi_count / HALF_CLK) << 20 :
159312cc39eSBin Meng 		(pi_count / HALF_CLK) << 8;
16038ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
16138ad43e4SBin Meng 
16238ad43e4SBin Meng 	/* Adjust PI_COUNT */
163312cc39eSBin Meng 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
16438ad43e4SBin Meng 
16538ad43e4SBin Meng 	/*
16638ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
16738ad43e4SBin Meng 	 * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
16838ad43e4SBin Meng 	 * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
16938ad43e4SBin Meng 	 */
170312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
171312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
172312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
173312cc39eSBin Meng 	msk = 0x3f000000;
17438ad43e4SBin Meng 	temp = pi_count << 24;
17538ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
17638ad43e4SBin Meng 
17738ad43e4SBin Meng 	/*
17838ad43e4SBin Meng 	 * DEADBAND
17938ad43e4SBin Meng 	 * BL0/1 -> B01DBCTL1[08/11] (+1 select)
18038ad43e4SBin Meng 	 * BL0/1 -> B01DBCTL1[02/05] (enable)
18138ad43e4SBin Meng 	 */
182312cc39eSBin Meng 	reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
183312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
18438ad43e4SBin Meng 	msk = 0x00;
18538ad43e4SBin Meng 	temp = 0x00;
18638ad43e4SBin Meng 
18738ad43e4SBin Meng 	/* enable */
188312cc39eSBin Meng 	msk |= (byte_lane & 1) ? (1 << 5) : (1 << 2);
18938ad43e4SBin Meng 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
19038ad43e4SBin Meng 		temp |= msk;
19138ad43e4SBin Meng 
19238ad43e4SBin Meng 	/* select */
193312cc39eSBin Meng 	msk |= (byte_lane & 1) ? (1 << 11) : (1 << 8);
19438ad43e4SBin Meng 	if (pi_count < EARLY_DB)
19538ad43e4SBin Meng 		temp |= msk;
19638ad43e4SBin Meng 
19738ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
19838ad43e4SBin Meng 
19938ad43e4SBin Meng 	/* error check */
200312cc39eSBin Meng 	if (pi_count > 0x3f) {
20138ad43e4SBin Meng 		training_message(channel, rank, byte_lane);
20238ad43e4SBin Meng 		mrc_post_code(0xee, 0xe0);
20338ad43e4SBin Meng 	}
20438ad43e4SBin Meng 
20538ad43e4SBin Meng 	LEAVEFN();
20638ad43e4SBin Meng }
20738ad43e4SBin Meng 
20838ad43e4SBin Meng /*
20938ad43e4SBin Meng  * This function will return the current RCVEN delay on the given
21038ad43e4SBin Meng  * channel, rank, byte_lane as an absolute PI count.
21138ad43e4SBin Meng  *
21238ad43e4SBin Meng  * (currently doesn't comprehend rank)
21338ad43e4SBin Meng  */
get_rcvn(uint8_t channel,uint8_t rank,uint8_t byte_lane)21438ad43e4SBin Meng uint32_t get_rcvn(uint8_t channel, uint8_t rank, uint8_t byte_lane)
21538ad43e4SBin Meng {
21638ad43e4SBin Meng 	uint32_t reg;
21738ad43e4SBin Meng 	uint32_t temp;
21838ad43e4SBin Meng 	uint32_t pi_count;
21938ad43e4SBin Meng 
22038ad43e4SBin Meng 	ENTERFN();
22138ad43e4SBin Meng 
22238ad43e4SBin Meng 	/*
22338ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
22438ad43e4SBin Meng 	 * BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
22538ad43e4SBin Meng 	 * BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
22638ad43e4SBin Meng 	 */
227312cc39eSBin Meng 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
228312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
22938ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
230312cc39eSBin Meng 	temp >>= (byte_lane & 1) ? 20 : 8;
231312cc39eSBin Meng 	temp &= 0xf;
23238ad43e4SBin Meng 
23338ad43e4SBin Meng 	/* Adjust PI_COUNT */
23438ad43e4SBin Meng 	pi_count = temp * HALF_CLK;
23538ad43e4SBin Meng 
23638ad43e4SBin Meng 	/*
23738ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
23838ad43e4SBin Meng 	 * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
23938ad43e4SBin Meng 	 * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
24038ad43e4SBin Meng 	 */
241312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
242312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
243312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
24438ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
24538ad43e4SBin Meng 	temp >>= 24;
246312cc39eSBin Meng 	temp &= 0x3f;
24738ad43e4SBin Meng 
24838ad43e4SBin Meng 	/* Adjust PI_COUNT */
24938ad43e4SBin Meng 	pi_count += temp;
25038ad43e4SBin Meng 
25138ad43e4SBin Meng 	LEAVEFN();
25238ad43e4SBin Meng 
25338ad43e4SBin Meng 	return pi_count;
25438ad43e4SBin Meng }
25538ad43e4SBin Meng 
25638ad43e4SBin Meng /*
25738ad43e4SBin Meng  * This function will program the RDQS delays based on an absolute
25838ad43e4SBin Meng  * amount of PIs.
25938ad43e4SBin Meng  *
26038ad43e4SBin Meng  * (currently doesn't comprehend rank)
26138ad43e4SBin Meng  */
set_rdqs(uint8_t channel,uint8_t rank,uint8_t byte_lane,uint32_t pi_count)26238ad43e4SBin Meng void set_rdqs(uint8_t channel, uint8_t rank,
26338ad43e4SBin Meng 	      uint8_t byte_lane, uint32_t pi_count)
26438ad43e4SBin Meng {
26538ad43e4SBin Meng 	uint32_t reg;
26638ad43e4SBin Meng 	uint32_t msk;
26738ad43e4SBin Meng 	uint32_t temp;
26838ad43e4SBin Meng 
26938ad43e4SBin Meng 	ENTERFN();
27038ad43e4SBin Meng 	DPF(D_TRN, "Rdqs ch%d rnk%d ln%d : pi=%03X\n",
27138ad43e4SBin Meng 	    channel, rank, byte_lane, pi_count);
27238ad43e4SBin Meng 
27338ad43e4SBin Meng 	/*
27438ad43e4SBin Meng 	 * PI (1/128 MCLK)
27538ad43e4SBin Meng 	 * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
27638ad43e4SBin Meng 	 * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
27738ad43e4SBin Meng 	 */
278312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE;
279312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
280312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
281312cc39eSBin Meng 	msk = 0x7f;
28238ad43e4SBin Meng 	temp = pi_count << 0;
28338ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
28438ad43e4SBin Meng 
28538ad43e4SBin Meng 	/* error check (shouldn't go above 0x3F) */
28638ad43e4SBin Meng 	if (pi_count > 0x47) {
28738ad43e4SBin Meng 		training_message(channel, rank, byte_lane);
28838ad43e4SBin Meng 		mrc_post_code(0xee, 0xe1);
28938ad43e4SBin Meng 	}
29038ad43e4SBin Meng 
29138ad43e4SBin Meng 	LEAVEFN();
29238ad43e4SBin Meng }
29338ad43e4SBin Meng 
29438ad43e4SBin Meng /*
29538ad43e4SBin Meng  * This function will return the current RDQS delay on the given
29638ad43e4SBin Meng  * channel, rank, byte_lane as an absolute PI count.
29738ad43e4SBin Meng  *
29838ad43e4SBin Meng  * (currently doesn't comprehend rank)
29938ad43e4SBin Meng  */
get_rdqs(uint8_t channel,uint8_t rank,uint8_t byte_lane)30038ad43e4SBin Meng uint32_t get_rdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane)
30138ad43e4SBin Meng {
30238ad43e4SBin Meng 	uint32_t reg;
30338ad43e4SBin Meng 	uint32_t temp;
30438ad43e4SBin Meng 	uint32_t pi_count;
30538ad43e4SBin Meng 
30638ad43e4SBin Meng 	ENTERFN();
30738ad43e4SBin Meng 
30838ad43e4SBin Meng 	/*
30938ad43e4SBin Meng 	 * PI (1/128 MCLK)
31038ad43e4SBin Meng 	 * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
31138ad43e4SBin Meng 	 * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
31238ad43e4SBin Meng 	 */
313312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE;
314312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
315312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
31638ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
31738ad43e4SBin Meng 
31838ad43e4SBin Meng 	/* Adjust PI_COUNT */
319312cc39eSBin Meng 	pi_count = temp & 0x7f;
32038ad43e4SBin Meng 
32138ad43e4SBin Meng 	LEAVEFN();
32238ad43e4SBin Meng 
32338ad43e4SBin Meng 	return pi_count;
32438ad43e4SBin Meng }
32538ad43e4SBin Meng 
32638ad43e4SBin Meng /*
32738ad43e4SBin Meng  * This function will program the WDQS delays based on an absolute
32838ad43e4SBin Meng  * amount of PIs.
32938ad43e4SBin Meng  *
33038ad43e4SBin Meng  * (currently doesn't comprehend rank)
33138ad43e4SBin Meng  */
set_wdqs(uint8_t channel,uint8_t rank,uint8_t byte_lane,uint32_t pi_count)33238ad43e4SBin Meng void set_wdqs(uint8_t channel, uint8_t rank,
33338ad43e4SBin Meng 	      uint8_t byte_lane, uint32_t pi_count)
33438ad43e4SBin Meng {
33538ad43e4SBin Meng 	uint32_t reg;
33638ad43e4SBin Meng 	uint32_t msk;
33738ad43e4SBin Meng 	uint32_t temp;
33838ad43e4SBin Meng 
33938ad43e4SBin Meng 	ENTERFN();
34038ad43e4SBin Meng 
34138ad43e4SBin Meng 	DPF(D_TRN, "Wdqs ch%d rnk%d ln%d : pi=%03X\n",
34238ad43e4SBin Meng 	    channel, rank, byte_lane, pi_count);
34338ad43e4SBin Meng 
34438ad43e4SBin Meng 	/*
34538ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
34638ad43e4SBin Meng 	 * BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
34738ad43e4SBin Meng 	 * BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
34838ad43e4SBin Meng 	 */
349312cc39eSBin Meng 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
350312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
351312cc39eSBin Meng 	msk = (byte_lane & 1) ? 0xf0000 : 0xf0;
35238ad43e4SBin Meng 	temp = pi_count / HALF_CLK;
353312cc39eSBin Meng 	temp <<= (byte_lane & 1) ? 16 : 4;
35438ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
35538ad43e4SBin Meng 
35638ad43e4SBin Meng 	/* Adjust PI_COUNT */
357312cc39eSBin Meng 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
35838ad43e4SBin Meng 
35938ad43e4SBin Meng 	/*
36038ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
36138ad43e4SBin Meng 	 * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
36238ad43e4SBin Meng 	 * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
36338ad43e4SBin Meng 	 */
364312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
365312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
366312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
367312cc39eSBin Meng 	msk = 0x3f0000;
36838ad43e4SBin Meng 	temp = pi_count << 16;
36938ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
37038ad43e4SBin Meng 
37138ad43e4SBin Meng 	/*
37238ad43e4SBin Meng 	 * DEADBAND
37338ad43e4SBin Meng 	 * BL0/1 -> B01DBCTL1[07/10] (+1 select)
37438ad43e4SBin Meng 	 * BL0/1 -> B01DBCTL1[01/04] (enable)
37538ad43e4SBin Meng 	 */
376312cc39eSBin Meng 	reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
377312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
37838ad43e4SBin Meng 	msk = 0x00;
37938ad43e4SBin Meng 	temp = 0x00;
38038ad43e4SBin Meng 
38138ad43e4SBin Meng 	/* enable */
382312cc39eSBin Meng 	msk |= (byte_lane & 1) ? (1 << 4) : (1 << 1);
38338ad43e4SBin Meng 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
38438ad43e4SBin Meng 		temp |= msk;
38538ad43e4SBin Meng 
38638ad43e4SBin Meng 	/* select */
387312cc39eSBin Meng 	msk |= (byte_lane & 1) ? (1 << 10) : (1 << 7);
38838ad43e4SBin Meng 	if (pi_count < EARLY_DB)
38938ad43e4SBin Meng 		temp |= msk;
39038ad43e4SBin Meng 
39138ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
39238ad43e4SBin Meng 
39338ad43e4SBin Meng 	/* error check */
394312cc39eSBin Meng 	if (pi_count > 0x3f) {
39538ad43e4SBin Meng 		training_message(channel, rank, byte_lane);
39638ad43e4SBin Meng 		mrc_post_code(0xee, 0xe2);
39738ad43e4SBin Meng 	}
39838ad43e4SBin Meng 
39938ad43e4SBin Meng 	LEAVEFN();
40038ad43e4SBin Meng }
40138ad43e4SBin Meng 
40238ad43e4SBin Meng /*
40338ad43e4SBin Meng  * This function will return the amount of WDQS delay on the given
40438ad43e4SBin Meng  * channel, rank, byte_lane as an absolute PI count.
40538ad43e4SBin Meng  *
40638ad43e4SBin Meng  * (currently doesn't comprehend rank)
40738ad43e4SBin Meng  */
get_wdqs(uint8_t channel,uint8_t rank,uint8_t byte_lane)40838ad43e4SBin Meng uint32_t get_wdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane)
40938ad43e4SBin Meng {
41038ad43e4SBin Meng 	uint32_t reg;
41138ad43e4SBin Meng 	uint32_t temp;
41238ad43e4SBin Meng 	uint32_t pi_count;
41338ad43e4SBin Meng 
41438ad43e4SBin Meng 	ENTERFN();
41538ad43e4SBin Meng 
41638ad43e4SBin Meng 	/*
41738ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
41838ad43e4SBin Meng 	 * BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
41938ad43e4SBin Meng 	 * BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
42038ad43e4SBin Meng 	 */
421312cc39eSBin Meng 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
422312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
42338ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
424312cc39eSBin Meng 	temp >>= (byte_lane & 1) ? 16 : 4;
425312cc39eSBin Meng 	temp &= 0xf;
42638ad43e4SBin Meng 
42738ad43e4SBin Meng 	/* Adjust PI_COUNT */
42838ad43e4SBin Meng 	pi_count = (temp * HALF_CLK);
42938ad43e4SBin Meng 
43038ad43e4SBin Meng 	/*
43138ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
43238ad43e4SBin Meng 	 * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
43338ad43e4SBin Meng 	 * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
43438ad43e4SBin Meng 	 */
435312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
436312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
437312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
43838ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
43938ad43e4SBin Meng 	temp >>= 16;
440312cc39eSBin Meng 	temp &= 0x3f;
44138ad43e4SBin Meng 
44238ad43e4SBin Meng 	/* Adjust PI_COUNT */
44338ad43e4SBin Meng 	pi_count += temp;
44438ad43e4SBin Meng 
44538ad43e4SBin Meng 	LEAVEFN();
44638ad43e4SBin Meng 
44738ad43e4SBin Meng 	return pi_count;
44838ad43e4SBin Meng }
44938ad43e4SBin Meng 
45038ad43e4SBin Meng /*
45138ad43e4SBin Meng  * This function will program the WDQ delays based on an absolute
45238ad43e4SBin Meng  * number of PIs.
45338ad43e4SBin Meng  *
45438ad43e4SBin Meng  * (currently doesn't comprehend rank)
45538ad43e4SBin Meng  */
set_wdq(uint8_t channel,uint8_t rank,uint8_t byte_lane,uint32_t pi_count)45638ad43e4SBin Meng void set_wdq(uint8_t channel, uint8_t rank,
45738ad43e4SBin Meng 	     uint8_t byte_lane, uint32_t pi_count)
45838ad43e4SBin Meng {
45938ad43e4SBin Meng 	uint32_t reg;
46038ad43e4SBin Meng 	uint32_t msk;
46138ad43e4SBin Meng 	uint32_t temp;
46238ad43e4SBin Meng 
46338ad43e4SBin Meng 	ENTERFN();
46438ad43e4SBin Meng 
46538ad43e4SBin Meng 	DPF(D_TRN, "Wdq ch%d rnk%d ln%d : pi=%03X\n",
46638ad43e4SBin Meng 	    channel, rank, byte_lane, pi_count);
46738ad43e4SBin Meng 
46838ad43e4SBin Meng 	/*
46938ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
47038ad43e4SBin Meng 	 * BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
47138ad43e4SBin Meng 	 * BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
47238ad43e4SBin Meng 	 */
473312cc39eSBin Meng 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
474312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
475312cc39eSBin Meng 	msk = (byte_lane & 1) ? 0xf000 : 0xf;
47638ad43e4SBin Meng 	temp = pi_count / HALF_CLK;
477312cc39eSBin Meng 	temp <<= (byte_lane & 1) ? 12 : 0;
47838ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
47938ad43e4SBin Meng 
48038ad43e4SBin Meng 	/* Adjust PI_COUNT */
481312cc39eSBin Meng 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
48238ad43e4SBin Meng 
48338ad43e4SBin Meng 	/*
48438ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
48538ad43e4SBin Meng 	 * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
48638ad43e4SBin Meng 	 * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
48738ad43e4SBin Meng 	 */
488312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
489312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
490312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
491312cc39eSBin Meng 	msk = 0x3f00;
49238ad43e4SBin Meng 	temp = pi_count << 8;
49338ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
49438ad43e4SBin Meng 
49538ad43e4SBin Meng 	/*
49638ad43e4SBin Meng 	 * DEADBAND
49738ad43e4SBin Meng 	 * BL0/1 -> B01DBCTL1[06/09] (+1 select)
49838ad43e4SBin Meng 	 * BL0/1 -> B01DBCTL1[00/03] (enable)
49938ad43e4SBin Meng 	 */
500312cc39eSBin Meng 	reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
501312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
50238ad43e4SBin Meng 	msk = 0x00;
50338ad43e4SBin Meng 	temp = 0x00;
50438ad43e4SBin Meng 
50538ad43e4SBin Meng 	/* enable */
506312cc39eSBin Meng 	msk |= (byte_lane & 1) ? (1 << 3) : (1 << 0);
50738ad43e4SBin Meng 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
50838ad43e4SBin Meng 		temp |= msk;
50938ad43e4SBin Meng 
51038ad43e4SBin Meng 	/* select */
511312cc39eSBin Meng 	msk |= (byte_lane & 1) ? (1 << 9) : (1 << 6);
51238ad43e4SBin Meng 	if (pi_count < EARLY_DB)
51338ad43e4SBin Meng 		temp |= msk;
51438ad43e4SBin Meng 
51538ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
51638ad43e4SBin Meng 
51738ad43e4SBin Meng 	/* error check */
518312cc39eSBin Meng 	if (pi_count > 0x3f) {
51938ad43e4SBin Meng 		training_message(channel, rank, byte_lane);
52038ad43e4SBin Meng 		mrc_post_code(0xee, 0xe3);
52138ad43e4SBin Meng 	}
52238ad43e4SBin Meng 
52338ad43e4SBin Meng 	LEAVEFN();
52438ad43e4SBin Meng }
52538ad43e4SBin Meng 
52638ad43e4SBin Meng /*
52738ad43e4SBin Meng  * This function will return the amount of WDQ delay on the given
52838ad43e4SBin Meng  * channel, rank, byte_lane as an absolute PI count.
52938ad43e4SBin Meng  *
53038ad43e4SBin Meng  * (currently doesn't comprehend rank)
53138ad43e4SBin Meng  */
get_wdq(uint8_t channel,uint8_t rank,uint8_t byte_lane)53238ad43e4SBin Meng uint32_t get_wdq(uint8_t channel, uint8_t rank, uint8_t byte_lane)
53338ad43e4SBin Meng {
53438ad43e4SBin Meng 	uint32_t reg;
53538ad43e4SBin Meng 	uint32_t temp;
53638ad43e4SBin Meng 	uint32_t pi_count;
53738ad43e4SBin Meng 
53838ad43e4SBin Meng 	ENTERFN();
53938ad43e4SBin Meng 
54038ad43e4SBin Meng 	/*
54138ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
54238ad43e4SBin Meng 	 * BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
54338ad43e4SBin Meng 	 * BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
54438ad43e4SBin Meng 	 */
545312cc39eSBin Meng 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
546312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET;
54738ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
548312cc39eSBin Meng 	temp >>= (byte_lane & 1) ? 12 : 0;
549312cc39eSBin Meng 	temp &= 0xf;
55038ad43e4SBin Meng 
55138ad43e4SBin Meng 	/* Adjust PI_COUNT */
55238ad43e4SBin Meng 	pi_count = temp * HALF_CLK;
55338ad43e4SBin Meng 
55438ad43e4SBin Meng 	/*
55538ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
55638ad43e4SBin Meng 	 * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
55738ad43e4SBin Meng 	 * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
55838ad43e4SBin Meng 	 */
559312cc39eSBin Meng 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
560312cc39eSBin Meng 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
561312cc39eSBin Meng 		channel * DDRIODQ_CH_OFFSET);
56238ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
56338ad43e4SBin Meng 	temp >>= 8;
564312cc39eSBin Meng 	temp &= 0x3f;
56538ad43e4SBin Meng 
56638ad43e4SBin Meng 	/* Adjust PI_COUNT */
56738ad43e4SBin Meng 	pi_count += temp;
56838ad43e4SBin Meng 
56938ad43e4SBin Meng 	LEAVEFN();
57038ad43e4SBin Meng 
57138ad43e4SBin Meng 	return pi_count;
57238ad43e4SBin Meng }
57338ad43e4SBin Meng 
57438ad43e4SBin Meng /*
57538ad43e4SBin Meng  * This function will program the WCMD delays based on an absolute
57638ad43e4SBin Meng  * number of PIs.
57738ad43e4SBin Meng  */
set_wcmd(uint8_t channel,uint32_t pi_count)57838ad43e4SBin Meng void set_wcmd(uint8_t channel, uint32_t pi_count)
57938ad43e4SBin Meng {
58038ad43e4SBin Meng 	uint32_t reg;
58138ad43e4SBin Meng 	uint32_t msk;
58238ad43e4SBin Meng 	uint32_t temp;
58338ad43e4SBin Meng 
58438ad43e4SBin Meng 	ENTERFN();
58538ad43e4SBin Meng 
58638ad43e4SBin Meng 	/*
58738ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
58838ad43e4SBin Meng 	 * CMDPTRREG[11:08] (0x0-0xF)
58938ad43e4SBin Meng 	 */
590312cc39eSBin Meng 	reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET;
591312cc39eSBin Meng 	msk = 0xf00;
59238ad43e4SBin Meng 	temp = pi_count / HALF_CLK;
59338ad43e4SBin Meng 	temp <<= 8;
59438ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
59538ad43e4SBin Meng 
59638ad43e4SBin Meng 	/* Adjust PI_COUNT */
597312cc39eSBin Meng 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
59838ad43e4SBin Meng 
59938ad43e4SBin Meng 	/*
60038ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
60138ad43e4SBin Meng 	 * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
60238ad43e4SBin Meng 	 * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
60338ad43e4SBin Meng 	 * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
60438ad43e4SBin Meng 	 * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
60538ad43e4SBin Meng 	 * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
60638ad43e4SBin Meng 	 * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
60738ad43e4SBin Meng 	 * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
60838ad43e4SBin Meng 	 * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
60938ad43e4SBin Meng 	 */
610312cc39eSBin Meng 	reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET;
611312cc39eSBin Meng 	msk = 0x3f3f3f3f;
61238ad43e4SBin Meng 	temp = (pi_count << 24) | (pi_count << 16) |
61338ad43e4SBin Meng 		(pi_count << 8) | (pi_count << 0);
61438ad43e4SBin Meng 
61538ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
616312cc39eSBin Meng 	reg = CMDDLLPICODER0 + channel * DDRIOCCC_CH_OFFSET;	/* PO */
61738ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
61838ad43e4SBin Meng 
61938ad43e4SBin Meng 	/*
62038ad43e4SBin Meng 	 * DEADBAND
62138ad43e4SBin Meng 	 * CMDCFGREG0[17] (+1 select)
62238ad43e4SBin Meng 	 * CMDCFGREG0[16] (enable)
62338ad43e4SBin Meng 	 */
624312cc39eSBin Meng 	reg = CMDCFGREG0 + channel * DDRIOCCC_CH_OFFSET;
62538ad43e4SBin Meng 	msk = 0x00;
62638ad43e4SBin Meng 	temp = 0x00;
62738ad43e4SBin Meng 
62838ad43e4SBin Meng 	/* enable */
629312cc39eSBin Meng 	msk |= (1 << 16);
63038ad43e4SBin Meng 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
63138ad43e4SBin Meng 		temp |= msk;
63238ad43e4SBin Meng 
63338ad43e4SBin Meng 	/* select */
634312cc39eSBin Meng 	msk |= (1 << 17);
63538ad43e4SBin Meng 	if (pi_count < EARLY_DB)
63638ad43e4SBin Meng 		temp |= msk;
63738ad43e4SBin Meng 
63838ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
63938ad43e4SBin Meng 
64038ad43e4SBin Meng 	/* error check */
641312cc39eSBin Meng 	if (pi_count > 0x3f)
64238ad43e4SBin Meng 		mrc_post_code(0xee, 0xe4);
64338ad43e4SBin Meng 
64438ad43e4SBin Meng 	LEAVEFN();
64538ad43e4SBin Meng }
64638ad43e4SBin Meng 
64738ad43e4SBin Meng /*
64838ad43e4SBin Meng  * This function will return the amount of WCMD delay on the given
64938ad43e4SBin Meng  * channel as an absolute PI count.
65038ad43e4SBin Meng  */
get_wcmd(uint8_t channel)65138ad43e4SBin Meng uint32_t get_wcmd(uint8_t channel)
65238ad43e4SBin Meng {
65338ad43e4SBin Meng 	uint32_t reg;
65438ad43e4SBin Meng 	uint32_t temp;
65538ad43e4SBin Meng 	uint32_t pi_count;
65638ad43e4SBin Meng 
65738ad43e4SBin Meng 	ENTERFN();
65838ad43e4SBin Meng 
65938ad43e4SBin Meng 	/*
66038ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
66138ad43e4SBin Meng 	 * CMDPTRREG[11:08] (0x0-0xF)
66238ad43e4SBin Meng 	 */
663312cc39eSBin Meng 	reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET;
66438ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
66538ad43e4SBin Meng 	temp >>= 8;
666312cc39eSBin Meng 	temp &= 0xf;
66738ad43e4SBin Meng 
66838ad43e4SBin Meng 	/* Adjust PI_COUNT */
66938ad43e4SBin Meng 	pi_count = temp * HALF_CLK;
67038ad43e4SBin Meng 
67138ad43e4SBin Meng 	/*
67238ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
67338ad43e4SBin Meng 	 * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
67438ad43e4SBin Meng 	 * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
67538ad43e4SBin Meng 	 * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
67638ad43e4SBin Meng 	 * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
67738ad43e4SBin Meng 	 * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
67838ad43e4SBin Meng 	 * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
67938ad43e4SBin Meng 	 * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
68038ad43e4SBin Meng 	 * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
68138ad43e4SBin Meng 	 */
682312cc39eSBin Meng 	reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET;
68338ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
68438ad43e4SBin Meng 	temp >>= 16;
685312cc39eSBin Meng 	temp &= 0x3f;
68638ad43e4SBin Meng 
68738ad43e4SBin Meng 	/* Adjust PI_COUNT */
68838ad43e4SBin Meng 	pi_count += temp;
68938ad43e4SBin Meng 
69038ad43e4SBin Meng 	LEAVEFN();
69138ad43e4SBin Meng 
69238ad43e4SBin Meng 	return pi_count;
69338ad43e4SBin Meng }
69438ad43e4SBin Meng 
69538ad43e4SBin Meng /*
69638ad43e4SBin Meng  * This function will program the WCLK delays based on an absolute
69738ad43e4SBin Meng  * number of PIs.
69838ad43e4SBin Meng  */
set_wclk(uint8_t channel,uint8_t rank,uint32_t pi_count)69938ad43e4SBin Meng void set_wclk(uint8_t channel, uint8_t rank, uint32_t pi_count)
70038ad43e4SBin Meng {
70138ad43e4SBin Meng 	uint32_t reg;
70238ad43e4SBin Meng 	uint32_t msk;
70338ad43e4SBin Meng 	uint32_t temp;
70438ad43e4SBin Meng 
70538ad43e4SBin Meng 	ENTERFN();
70638ad43e4SBin Meng 
70738ad43e4SBin Meng 	/*
70838ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
70938ad43e4SBin Meng 	 * CCPTRREG[15:12] -> CLK1 (0x0-0xF)
71038ad43e4SBin Meng 	 * CCPTRREG[11:08] -> CLK0 (0x0-0xF)
71138ad43e4SBin Meng 	 */
712312cc39eSBin Meng 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
713312cc39eSBin Meng 	msk = 0xff00;
71438ad43e4SBin Meng 	temp = ((pi_count / HALF_CLK) << 12) | ((pi_count / HALF_CLK) << 8);
71538ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
71638ad43e4SBin Meng 
71738ad43e4SBin Meng 	/* Adjust PI_COUNT */
718312cc39eSBin Meng 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
71938ad43e4SBin Meng 
72038ad43e4SBin Meng 	/*
72138ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
72238ad43e4SBin Meng 	 * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
72338ad43e4SBin Meng 	 * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
72438ad43e4SBin Meng 	 */
72538ad43e4SBin Meng 	reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0;
72638ad43e4SBin Meng 	reg += (channel * DDRIOCCC_CH_OFFSET);
727312cc39eSBin Meng 	msk = 0x3f3f00;
72838ad43e4SBin Meng 	temp = (pi_count << 16) | (pi_count << 8);
72938ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
730312cc39eSBin Meng 
73138ad43e4SBin Meng 	reg = rank ? ECCB1DLLPICODER1 : ECCB1DLLPICODER1;
73238ad43e4SBin Meng 	reg += (channel * DDRIOCCC_CH_OFFSET);
73338ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
734312cc39eSBin Meng 
73538ad43e4SBin Meng 	reg = rank ? ECCB1DLLPICODER2 : ECCB1DLLPICODER2;
73638ad43e4SBin Meng 	reg += (channel * DDRIOCCC_CH_OFFSET);
73738ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
738312cc39eSBin Meng 
73938ad43e4SBin Meng 	reg = rank ? ECCB1DLLPICODER3 : ECCB1DLLPICODER3;
74038ad43e4SBin Meng 	reg += (channel * DDRIOCCC_CH_OFFSET);
74138ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
74238ad43e4SBin Meng 
74338ad43e4SBin Meng 	/*
74438ad43e4SBin Meng 	 * DEADBAND
74538ad43e4SBin Meng 	 * CCCFGREG1[11:08] (+1 select)
74638ad43e4SBin Meng 	 * CCCFGREG1[03:00] (enable)
74738ad43e4SBin Meng 	 */
748312cc39eSBin Meng 	reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET;
74938ad43e4SBin Meng 	msk = 0x00;
75038ad43e4SBin Meng 	temp = 0x00;
75138ad43e4SBin Meng 
75238ad43e4SBin Meng 	/* enable */
753312cc39eSBin Meng 	msk |= 0xf;
75438ad43e4SBin Meng 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
75538ad43e4SBin Meng 		temp |= msk;
75638ad43e4SBin Meng 
75738ad43e4SBin Meng 	/* select */
758312cc39eSBin Meng 	msk |= 0xf00;
75938ad43e4SBin Meng 	if (pi_count < EARLY_DB)
76038ad43e4SBin Meng 		temp |= msk;
76138ad43e4SBin Meng 
76238ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
76338ad43e4SBin Meng 
76438ad43e4SBin Meng 	/* error check */
765312cc39eSBin Meng 	if (pi_count > 0x3f)
76638ad43e4SBin Meng 		mrc_post_code(0xee, 0xe5);
76738ad43e4SBin Meng 
76838ad43e4SBin Meng 	LEAVEFN();
76938ad43e4SBin Meng }
77038ad43e4SBin Meng 
77138ad43e4SBin Meng /*
77238ad43e4SBin Meng  * This function will return the amout of WCLK delay on the given
77338ad43e4SBin Meng  * channel, rank as an absolute PI count.
77438ad43e4SBin Meng  */
get_wclk(uint8_t channel,uint8_t rank)77538ad43e4SBin Meng uint32_t get_wclk(uint8_t channel, uint8_t rank)
77638ad43e4SBin Meng {
77738ad43e4SBin Meng 	uint32_t reg;
77838ad43e4SBin Meng 	uint32_t temp;
77938ad43e4SBin Meng 	uint32_t pi_count;
78038ad43e4SBin Meng 
78138ad43e4SBin Meng 	ENTERFN();
78238ad43e4SBin Meng 
78338ad43e4SBin Meng 	/*
78438ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
78538ad43e4SBin Meng 	 * CCPTRREG[15:12] -> CLK1 (0x0-0xF)
78638ad43e4SBin Meng 	 * CCPTRREG[11:08] -> CLK0 (0x0-0xF)
78738ad43e4SBin Meng 	 */
788312cc39eSBin Meng 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
78938ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
79038ad43e4SBin Meng 	temp >>= rank ? 12 : 8;
791312cc39eSBin Meng 	temp &= 0xf;
79238ad43e4SBin Meng 
79338ad43e4SBin Meng 	/* Adjust PI_COUNT */
79438ad43e4SBin Meng 	pi_count = temp * HALF_CLK;
79538ad43e4SBin Meng 
79638ad43e4SBin Meng 	/*
79738ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
79838ad43e4SBin Meng 	 * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
79938ad43e4SBin Meng 	 * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
80038ad43e4SBin Meng 	 */
80138ad43e4SBin Meng 	reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0;
80238ad43e4SBin Meng 	reg += (channel * DDRIOCCC_CH_OFFSET);
80338ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
80438ad43e4SBin Meng 	temp >>= rank ? 16 : 8;
805312cc39eSBin Meng 	temp &= 0x3f;
80638ad43e4SBin Meng 
80738ad43e4SBin Meng 	pi_count += temp;
80838ad43e4SBin Meng 
80938ad43e4SBin Meng 	LEAVEFN();
81038ad43e4SBin Meng 
81138ad43e4SBin Meng 	return pi_count;
81238ad43e4SBin Meng }
81338ad43e4SBin Meng 
81438ad43e4SBin Meng /*
81538ad43e4SBin Meng  * This function will program the WCTL delays based on an absolute
81638ad43e4SBin Meng  * number of PIs.
81738ad43e4SBin Meng  *
81838ad43e4SBin Meng  * (currently doesn't comprehend rank)
81938ad43e4SBin Meng  */
set_wctl(uint8_t channel,uint8_t rank,uint32_t pi_count)82038ad43e4SBin Meng void set_wctl(uint8_t channel, uint8_t rank, uint32_t pi_count)
82138ad43e4SBin Meng {
82238ad43e4SBin Meng 	uint32_t reg;
82338ad43e4SBin Meng 	uint32_t msk;
82438ad43e4SBin Meng 	uint32_t temp;
82538ad43e4SBin Meng 
82638ad43e4SBin Meng 	ENTERFN();
82738ad43e4SBin Meng 
82838ad43e4SBin Meng 	/*
82938ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
83038ad43e4SBin Meng 	 * CCPTRREG[31:28] (0x0-0xF)
83138ad43e4SBin Meng 	 * CCPTRREG[27:24] (0x0-0xF)
83238ad43e4SBin Meng 	 */
833312cc39eSBin Meng 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
834312cc39eSBin Meng 	msk = 0xff000000;
83538ad43e4SBin Meng 	temp = ((pi_count / HALF_CLK) << 28) | ((pi_count / HALF_CLK) << 24);
83638ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
83738ad43e4SBin Meng 
83838ad43e4SBin Meng 	/* Adjust PI_COUNT */
839312cc39eSBin Meng 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
84038ad43e4SBin Meng 
84138ad43e4SBin Meng 	/*
84238ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
84338ad43e4SBin Meng 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
84438ad43e4SBin Meng 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
84538ad43e4SBin Meng 	 */
846312cc39eSBin Meng 	reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET;
847312cc39eSBin Meng 	msk = 0x3f000000;
84838ad43e4SBin Meng 	temp = (pi_count << 24);
84938ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
850312cc39eSBin Meng 
851312cc39eSBin Meng 	reg = ECCB1DLLPICODER1 + channel * DDRIOCCC_CH_OFFSET;
85238ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
853312cc39eSBin Meng 
854312cc39eSBin Meng 	reg = ECCB1DLLPICODER2 + channel * DDRIOCCC_CH_OFFSET;
85538ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
856312cc39eSBin Meng 
857312cc39eSBin Meng 	reg = ECCB1DLLPICODER3 + channel * DDRIOCCC_CH_OFFSET;
85838ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
85938ad43e4SBin Meng 
86038ad43e4SBin Meng 	/*
86138ad43e4SBin Meng 	 * DEADBAND
86238ad43e4SBin Meng 	 * CCCFGREG1[13:12] (+1 select)
86338ad43e4SBin Meng 	 * CCCFGREG1[05:04] (enable)
86438ad43e4SBin Meng 	 */
865312cc39eSBin Meng 	reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET;
86638ad43e4SBin Meng 	msk = 0x00;
86738ad43e4SBin Meng 	temp = 0x00;
86838ad43e4SBin Meng 
86938ad43e4SBin Meng 	/* enable */
870312cc39eSBin Meng 	msk |= 0x30;
87138ad43e4SBin Meng 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
87238ad43e4SBin Meng 		temp |= msk;
87338ad43e4SBin Meng 
87438ad43e4SBin Meng 	/* select */
875312cc39eSBin Meng 	msk |= 0x3000;
87638ad43e4SBin Meng 	if (pi_count < EARLY_DB)
87738ad43e4SBin Meng 		temp |= msk;
87838ad43e4SBin Meng 
87938ad43e4SBin Meng 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
88038ad43e4SBin Meng 
88138ad43e4SBin Meng 	/* error check */
882312cc39eSBin Meng 	if (pi_count > 0x3f)
88338ad43e4SBin Meng 		mrc_post_code(0xee, 0xe6);
88438ad43e4SBin Meng 
88538ad43e4SBin Meng 	LEAVEFN();
88638ad43e4SBin Meng }
88738ad43e4SBin Meng 
88838ad43e4SBin Meng /*
88938ad43e4SBin Meng  * This function will return the amount of WCTL delay on the given
89038ad43e4SBin Meng  * channel, rank as an absolute PI count.
89138ad43e4SBin Meng  *
89238ad43e4SBin Meng  * (currently doesn't comprehend rank)
89338ad43e4SBin Meng  */
get_wctl(uint8_t channel,uint8_t rank)89438ad43e4SBin Meng uint32_t get_wctl(uint8_t channel, uint8_t rank)
89538ad43e4SBin Meng {
89638ad43e4SBin Meng 	uint32_t reg;
89738ad43e4SBin Meng 	uint32_t temp;
89838ad43e4SBin Meng 	uint32_t pi_count;
89938ad43e4SBin Meng 
90038ad43e4SBin Meng 	ENTERFN();
90138ad43e4SBin Meng 
90238ad43e4SBin Meng 	/*
90338ad43e4SBin Meng 	 * RDPTR (1/2 MCLK, 64 PIs)
90438ad43e4SBin Meng 	 * CCPTRREG[31:28] (0x0-0xF)
90538ad43e4SBin Meng 	 * CCPTRREG[27:24] (0x0-0xF)
90638ad43e4SBin Meng 	 */
907312cc39eSBin Meng 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
90838ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
90938ad43e4SBin Meng 	temp >>= 24;
910312cc39eSBin Meng 	temp &= 0xf;
91138ad43e4SBin Meng 
91238ad43e4SBin Meng 	/* Adjust PI_COUNT */
91338ad43e4SBin Meng 	pi_count = temp * HALF_CLK;
91438ad43e4SBin Meng 
91538ad43e4SBin Meng 	/*
91638ad43e4SBin Meng 	 * PI (1/64 MCLK, 1 PIs)
91738ad43e4SBin Meng 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
91838ad43e4SBin Meng 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
91938ad43e4SBin Meng 	 */
920312cc39eSBin Meng 	reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET;
92138ad43e4SBin Meng 	temp = msg_port_alt_read(DDRPHY, reg);
92238ad43e4SBin Meng 	temp >>= 24;
923312cc39eSBin Meng 	temp &= 0x3f;
92438ad43e4SBin Meng 
92538ad43e4SBin Meng 	/* Adjust PI_COUNT */
92638ad43e4SBin Meng 	pi_count += temp;
92738ad43e4SBin Meng 
92838ad43e4SBin Meng 	LEAVEFN();
92938ad43e4SBin Meng 
93038ad43e4SBin Meng 	return pi_count;
93138ad43e4SBin Meng }
93238ad43e4SBin Meng 
93338ad43e4SBin Meng /*
93438ad43e4SBin Meng  * This function will program the internal Vref setting in a given
93538ad43e4SBin Meng  * byte lane in a given channel.
93638ad43e4SBin Meng  */
set_vref(uint8_t channel,uint8_t byte_lane,uint32_t setting)93738ad43e4SBin Meng void set_vref(uint8_t channel, uint8_t byte_lane, uint32_t setting)
93838ad43e4SBin Meng {
939312cc39eSBin Meng 	uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL;
94038ad43e4SBin Meng 
94138ad43e4SBin Meng 	ENTERFN();
94238ad43e4SBin Meng 
94338ad43e4SBin Meng 	DPF(D_TRN, "Vref ch%d ln%d : val=%03X\n",
94438ad43e4SBin Meng 	    channel, byte_lane, setting);
94538ad43e4SBin Meng 
946312cc39eSBin Meng 	mrc_alt_write_mask(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET +
947312cc39eSBin Meng 		(byte_lane >> 1) * DDRIODQ_BL_OFFSET,
948312cc39eSBin Meng 		vref_codes[setting] << 2, 0xfc);
94938ad43e4SBin Meng 
95038ad43e4SBin Meng 	/*
95138ad43e4SBin Meng 	 * need to wait ~300ns for Vref to settle
95238ad43e4SBin Meng 	 * (check that this is necessary)
95338ad43e4SBin Meng 	 */
95438ad43e4SBin Meng 	delay_n(300);
95538ad43e4SBin Meng 
95638ad43e4SBin Meng 	/* ??? may need to clear pointers ??? */
95738ad43e4SBin Meng 
95838ad43e4SBin Meng 	LEAVEFN();
95938ad43e4SBin Meng }
96038ad43e4SBin Meng 
96138ad43e4SBin Meng /*
96238ad43e4SBin Meng  * This function will return the internal Vref setting for the given
96338ad43e4SBin Meng  * channel, byte_lane.
96438ad43e4SBin Meng  */
get_vref(uint8_t channel,uint8_t byte_lane)96538ad43e4SBin Meng uint32_t get_vref(uint8_t channel, uint8_t byte_lane)
96638ad43e4SBin Meng {
96738ad43e4SBin Meng 	uint8_t j;
96838ad43e4SBin Meng 	uint32_t ret_val = sizeof(vref_codes) / 2;
969312cc39eSBin Meng 	uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL;
97038ad43e4SBin Meng 	uint32_t temp;
97138ad43e4SBin Meng 
97238ad43e4SBin Meng 	ENTERFN();
97338ad43e4SBin Meng 
974312cc39eSBin Meng 	temp = msg_port_alt_read(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET +
975312cc39eSBin Meng 		(byte_lane >> 1) * DDRIODQ_BL_OFFSET);
97638ad43e4SBin Meng 	temp >>= 2;
977312cc39eSBin Meng 	temp &= 0x3f;
97838ad43e4SBin Meng 
97938ad43e4SBin Meng 	for (j = 0; j < sizeof(vref_codes); j++) {
98038ad43e4SBin Meng 		if (vref_codes[j] == temp) {
98138ad43e4SBin Meng 			ret_val = j;
98238ad43e4SBin Meng 			break;
98338ad43e4SBin Meng 		}
98438ad43e4SBin Meng 	}
98538ad43e4SBin Meng 
98638ad43e4SBin Meng 	LEAVEFN();
98738ad43e4SBin Meng 
98838ad43e4SBin Meng 	return ret_val;
98938ad43e4SBin Meng }
99038ad43e4SBin Meng 
99138ad43e4SBin Meng /*
99238ad43e4SBin Meng  * This function will return a 32-bit address in the desired
99338ad43e4SBin Meng  * channel and rank.
99438ad43e4SBin Meng  */
get_addr(uint8_t channel,uint8_t rank)99538ad43e4SBin Meng uint32_t get_addr(uint8_t channel, uint8_t rank)
99638ad43e4SBin Meng {
997312cc39eSBin Meng 	uint32_t offset = 32 * 1024 * 1024;	/* 32MB */
99838ad43e4SBin Meng 
99938ad43e4SBin Meng 	/* Begin product specific code */
100038ad43e4SBin Meng 	if (channel > 0) {
100138ad43e4SBin Meng 		DPF(D_ERROR, "ILLEGAL CHANNEL\n");
100238ad43e4SBin Meng 		DEAD_LOOP();
100338ad43e4SBin Meng 	}
100438ad43e4SBin Meng 
100538ad43e4SBin Meng 	if (rank > 1) {
100638ad43e4SBin Meng 		DPF(D_ERROR, "ILLEGAL RANK\n");
100738ad43e4SBin Meng 		DEAD_LOOP();
100838ad43e4SBin Meng 	}
100938ad43e4SBin Meng 
101038ad43e4SBin Meng 	/* use 256MB lowest density as per DRP == 0x0003 */
101138ad43e4SBin Meng 	offset += rank * (256 * 1024 * 1024);
101238ad43e4SBin Meng 
101338ad43e4SBin Meng 	return offset;
101438ad43e4SBin Meng }
101538ad43e4SBin Meng 
101638ad43e4SBin Meng /*
101738ad43e4SBin Meng  * This function will sample the DQTRAINSTS registers in the given
101838ad43e4SBin Meng  * channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'.
101938ad43e4SBin Meng  *
102038ad43e4SBin Meng  * It will return an encoded 32-bit date in which each bit corresponds to
102138ad43e4SBin Meng  * the sampled value on the byte lane.
102238ad43e4SBin Meng  */
sample_dqs(struct mrc_params * mrc_params,uint8_t channel,uint8_t rank,bool rcvn)102338ad43e4SBin Meng uint32_t sample_dqs(struct mrc_params *mrc_params, uint8_t channel,
102438ad43e4SBin Meng 		    uint8_t rank, bool rcvn)
102538ad43e4SBin Meng {
102638ad43e4SBin Meng 	uint8_t j;	/* just a counter */
102738ad43e4SBin Meng 	uint8_t bl;	/* which BL in the module (always 2 per module) */
102838ad43e4SBin Meng 	uint8_t bl_grp;	/* which BL module */
102938ad43e4SBin Meng 	/* byte lane divisor */
103038ad43e4SBin Meng 	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
103138ad43e4SBin Meng 	uint32_t msk[2];	/* BLx in module */
103238ad43e4SBin Meng 	/* DQTRAINSTS register contents for each sample */
103338ad43e4SBin Meng 	uint32_t sampled_val[SAMPLE_SIZE];
103438ad43e4SBin Meng 	uint32_t num_0s;	/* tracks the number of '0' samples */
103538ad43e4SBin Meng 	uint32_t num_1s;	/* tracks the number of '1' samples */
103638ad43e4SBin Meng 	uint32_t ret_val = 0x00;	/* assume all '0' samples */
103738ad43e4SBin Meng 	uint32_t address = get_addr(channel, rank);
103838ad43e4SBin Meng 
103938ad43e4SBin Meng 	/* initialise msk[] */
1040312cc39eSBin Meng 	msk[0] = rcvn ? (1 << 1) : (1 << 9);	/* BL0 */
1041312cc39eSBin Meng 	msk[1] = rcvn ? (1 << 0) : (1 << 8);	/* BL1 */
104238ad43e4SBin Meng 
104338ad43e4SBin Meng 	/* cycle through each byte lane group */
104438ad43e4SBin Meng 	for (bl_grp = 0; bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; bl_grp++) {
104538ad43e4SBin Meng 		/* take SAMPLE_SIZE samples */
104638ad43e4SBin Meng 		for (j = 0; j < SAMPLE_SIZE; j++) {
104738ad43e4SBin Meng 			hte_mem_op(address, mrc_params->first_run,
104838ad43e4SBin Meng 				   rcvn ? 0 : 1);
104938ad43e4SBin Meng 			mrc_params->first_run = 0;
105038ad43e4SBin Meng 
105138ad43e4SBin Meng 			/*
105238ad43e4SBin Meng 			 * record the contents of the proper
105338ad43e4SBin Meng 			 * DQTRAINSTS register
105438ad43e4SBin Meng 			 */
105538ad43e4SBin Meng 			sampled_val[j] = msg_port_alt_read(DDRPHY,
1056312cc39eSBin Meng 				DQTRAINSTS +
1057312cc39eSBin Meng 				bl_grp * DDRIODQ_BL_OFFSET +
1058312cc39eSBin Meng 				channel * DDRIODQ_CH_OFFSET);
105938ad43e4SBin Meng 		}
106038ad43e4SBin Meng 
106138ad43e4SBin Meng 		/*
106238ad43e4SBin Meng 		 * look for a majority value (SAMPLE_SIZE / 2) + 1
106338ad43e4SBin Meng 		 * on the byte lane and set that value in the corresponding
106438ad43e4SBin Meng 		 * ret_val bit
106538ad43e4SBin Meng 		 */
106638ad43e4SBin Meng 		for (bl = 0; bl < 2; bl++) {
106738ad43e4SBin Meng 			num_0s = 0x00;	/* reset '0' tracker for byte lane */
106838ad43e4SBin Meng 			num_1s = 0x00;	/* reset '1' tracker for byte lane */
106938ad43e4SBin Meng 			for (j = 0; j < SAMPLE_SIZE; j++) {
107038ad43e4SBin Meng 				if (sampled_val[j] & msk[bl])
107138ad43e4SBin Meng 					num_1s++;
107238ad43e4SBin Meng 				else
107338ad43e4SBin Meng 					num_0s++;
107438ad43e4SBin Meng 			}
107538ad43e4SBin Meng 		if (num_1s > num_0s)
1076312cc39eSBin Meng 			ret_val |= (1 << (bl + bl_grp * 2));
107738ad43e4SBin Meng 		}
107838ad43e4SBin Meng 	}
107938ad43e4SBin Meng 
108038ad43e4SBin Meng 	/*
108138ad43e4SBin Meng 	 * "ret_val.0" contains the status of BL0
108238ad43e4SBin Meng 	 * "ret_val.1" contains the status of BL1
108338ad43e4SBin Meng 	 * "ret_val.2" contains the status of BL2
108438ad43e4SBin Meng 	 * etc.
108538ad43e4SBin Meng 	 */
108638ad43e4SBin Meng 	return ret_val;
108738ad43e4SBin Meng }
108838ad43e4SBin Meng 
108938ad43e4SBin Meng /* This function will find the rising edge transition on RCVN or WDQS */
find_rising_edge(struct mrc_params * mrc_params,uint32_t delay[],uint8_t channel,uint8_t rank,bool rcvn)109038ad43e4SBin Meng void find_rising_edge(struct mrc_params *mrc_params, uint32_t delay[],
109138ad43e4SBin Meng 		      uint8_t channel, uint8_t rank, bool rcvn)
109238ad43e4SBin Meng {
109338ad43e4SBin Meng 	bool all_edges_found;	/* determines stop condition */
109438ad43e4SBin Meng 	bool direction[NUM_BYTE_LANES];	/* direction indicator */
109538ad43e4SBin Meng 	uint8_t sample;	/* sample counter */
109638ad43e4SBin Meng 	uint8_t bl;	/* byte lane counter */
109738ad43e4SBin Meng 	/* byte lane divisor */
109838ad43e4SBin Meng 	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
109938ad43e4SBin Meng 	uint32_t sample_result[SAMPLE_CNT];	/* results of sample_dqs() */
110038ad43e4SBin Meng 	uint32_t temp;
110138ad43e4SBin Meng 	uint32_t transition_pattern;
110238ad43e4SBin Meng 
110338ad43e4SBin Meng 	ENTERFN();
110438ad43e4SBin Meng 
110538ad43e4SBin Meng 	/* select hte and request initial configuration */
110638ad43e4SBin Meng 	select_hte();
110738ad43e4SBin Meng 	mrc_params->first_run = 1;
110838ad43e4SBin Meng 
110938ad43e4SBin Meng 	/* Take 3 sample points (T1,T2,T3) to obtain a transition pattern */
111038ad43e4SBin Meng 	for (sample = 0; sample < SAMPLE_CNT; sample++) {
111138ad43e4SBin Meng 		/* program the desired delays for sample */
111238ad43e4SBin Meng 		for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
111338ad43e4SBin Meng 			/* increase sample delay by 26 PI (0.2 CLK) */
111438ad43e4SBin Meng 			if (rcvn) {
111538ad43e4SBin Meng 				set_rcvn(channel, rank, bl,
1116312cc39eSBin Meng 					 delay[bl] + sample * SAMPLE_DLY);
111738ad43e4SBin Meng 			} else {
111838ad43e4SBin Meng 				set_wdqs(channel, rank, bl,
1119312cc39eSBin Meng 					 delay[bl] + sample * SAMPLE_DLY);
112038ad43e4SBin Meng 			}
112138ad43e4SBin Meng 		}
112238ad43e4SBin Meng 
112338ad43e4SBin Meng 		/* take samples (Tsample_i) */
112438ad43e4SBin Meng 		sample_result[sample] = sample_dqs(mrc_params,
112538ad43e4SBin Meng 			channel, rank, rcvn);
112638ad43e4SBin Meng 
112738ad43e4SBin Meng 		DPF(D_TRN,
112838ad43e4SBin Meng 		    "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n",
1129312cc39eSBin Meng 		    rcvn ? "RCVN" : "WDQS", channel, rank, sample,
113038ad43e4SBin Meng 		    sample * SAMPLE_DLY, sample_result[sample]);
113138ad43e4SBin Meng 	}
113238ad43e4SBin Meng 
113338ad43e4SBin Meng 	/*
113438ad43e4SBin Meng 	 * This pattern will help determine where we landed and ultimately
113538ad43e4SBin Meng 	 * how to place RCVEN/WDQS.
113638ad43e4SBin Meng 	 */
1137312cc39eSBin Meng 	for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
113838ad43e4SBin Meng 		/* build transition_pattern (MSB is 1st sample) */
113938ad43e4SBin Meng 		transition_pattern = 0;
114038ad43e4SBin Meng 		for (sample = 0; sample < SAMPLE_CNT; sample++) {
114138ad43e4SBin Meng 			transition_pattern |=
114238ad43e4SBin Meng 				((sample_result[sample] & (1 << bl)) >> bl) <<
114338ad43e4SBin Meng 				(SAMPLE_CNT - 1 - sample);
114438ad43e4SBin Meng 		}
114538ad43e4SBin Meng 
114638ad43e4SBin Meng 		DPF(D_TRN, "=== transition pattern %d\n", transition_pattern);
114738ad43e4SBin Meng 
114838ad43e4SBin Meng 		/*
114938ad43e4SBin Meng 		 * set up to look for rising edge based on
115038ad43e4SBin Meng 		 * transition_pattern
115138ad43e4SBin Meng 		 */
115238ad43e4SBin Meng 		switch (transition_pattern) {
115338ad43e4SBin Meng 		case 0:	/* sampled 0->0->0 */
115438ad43e4SBin Meng 			/* move forward from T3 looking for 0->1 */
115538ad43e4SBin Meng 			delay[bl] += 2 * SAMPLE_DLY;
115638ad43e4SBin Meng 			direction[bl] = FORWARD;
115738ad43e4SBin Meng 			break;
115838ad43e4SBin Meng 		case 1:	/* sampled 0->0->1 */
115938ad43e4SBin Meng 		case 5:	/* sampled 1->0->1 (bad duty cycle) *HSD#237503* */
116038ad43e4SBin Meng 			/* move forward from T2 looking for 0->1 */
116138ad43e4SBin Meng 			delay[bl] += 1 * SAMPLE_DLY;
116238ad43e4SBin Meng 			direction[bl] = FORWARD;
116338ad43e4SBin Meng 			break;
116438ad43e4SBin Meng 		case 2:	/* sampled 0->1->0 (bad duty cycle) *HSD#237503* */
116538ad43e4SBin Meng 		case 3:	/* sampled 0->1->1 */
116638ad43e4SBin Meng 			/* move forward from T1 looking for 0->1 */
116738ad43e4SBin Meng 			delay[bl] += 0 * SAMPLE_DLY;
116838ad43e4SBin Meng 			direction[bl] = FORWARD;
116938ad43e4SBin Meng 			break;
117038ad43e4SBin Meng 		case 4:	/* sampled 1->0->0 (assumes BL8, HSD#234975) */
117138ad43e4SBin Meng 			/* move forward from T3 looking for 0->1 */
117238ad43e4SBin Meng 			delay[bl] += 2 * SAMPLE_DLY;
117338ad43e4SBin Meng 			direction[bl] = FORWARD;
117438ad43e4SBin Meng 			break;
117538ad43e4SBin Meng 		case 6:	/* sampled 1->1->0 */
117638ad43e4SBin Meng 		case 7:	/* sampled 1->1->1 */
117738ad43e4SBin Meng 			/* move backward from T1 looking for 1->0 */
117838ad43e4SBin Meng 			delay[bl] += 0 * SAMPLE_DLY;
117938ad43e4SBin Meng 			direction[bl] = BACKWARD;
118038ad43e4SBin Meng 			break;
118138ad43e4SBin Meng 		default:
118238ad43e4SBin Meng 			mrc_post_code(0xee, 0xee);
118338ad43e4SBin Meng 			break;
118438ad43e4SBin Meng 		}
118538ad43e4SBin Meng 
118638ad43e4SBin Meng 		/* program delays */
118738ad43e4SBin Meng 		if (rcvn)
118838ad43e4SBin Meng 			set_rcvn(channel, rank, bl, delay[bl]);
118938ad43e4SBin Meng 		else
119038ad43e4SBin Meng 			set_wdqs(channel, rank, bl, delay[bl]);
119138ad43e4SBin Meng 	}
119238ad43e4SBin Meng 
119338ad43e4SBin Meng 	/*
119438ad43e4SBin Meng 	 * Based on the observed transition pattern on the byte lane,
119538ad43e4SBin Meng 	 * begin looking for a rising edge with single PI granularity.
119638ad43e4SBin Meng 	 */
119738ad43e4SBin Meng 	do {
119838ad43e4SBin Meng 		all_edges_found = true;	/* assume all byte lanes passed */
119938ad43e4SBin Meng 		/* take a sample */
120038ad43e4SBin Meng 		temp = sample_dqs(mrc_params, channel, rank, rcvn);
120138ad43e4SBin Meng 		/* check all each byte lane for proper edge */
1202312cc39eSBin Meng 		for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
120338ad43e4SBin Meng 			if (temp & (1 << bl)) {
120438ad43e4SBin Meng 				/* sampled "1" */
120538ad43e4SBin Meng 				if (direction[bl] == BACKWARD) {
120638ad43e4SBin Meng 					/*
120738ad43e4SBin Meng 					 * keep looking for edge
120838ad43e4SBin Meng 					 * on this byte lane
120938ad43e4SBin Meng 					 */
121038ad43e4SBin Meng 					all_edges_found = false;
121138ad43e4SBin Meng 					delay[bl] -= 1;
121238ad43e4SBin Meng 					if (rcvn) {
121338ad43e4SBin Meng 						set_rcvn(channel, rank,
121438ad43e4SBin Meng 							 bl, delay[bl]);
121538ad43e4SBin Meng 					} else {
121638ad43e4SBin Meng 						set_wdqs(channel, rank,
121738ad43e4SBin Meng 							 bl, delay[bl]);
121838ad43e4SBin Meng 					}
121938ad43e4SBin Meng 				}
122038ad43e4SBin Meng 			} else {
122138ad43e4SBin Meng 				/* sampled "0" */
122238ad43e4SBin Meng 				if (direction[bl] == FORWARD) {
122338ad43e4SBin Meng 					/*
122438ad43e4SBin Meng 					 * keep looking for edge
122538ad43e4SBin Meng 					 * on this byte lane
122638ad43e4SBin Meng 					 */
122738ad43e4SBin Meng 					all_edges_found = false;
122838ad43e4SBin Meng 					delay[bl] += 1;
122938ad43e4SBin Meng 					if (rcvn) {
123038ad43e4SBin Meng 						set_rcvn(channel, rank,
123138ad43e4SBin Meng 							 bl, delay[bl]);
123238ad43e4SBin Meng 					} else {
123338ad43e4SBin Meng 						set_wdqs(channel, rank,
123438ad43e4SBin Meng 							 bl, delay[bl]);
123538ad43e4SBin Meng 					}
123638ad43e4SBin Meng 				}
123738ad43e4SBin Meng 			}
123838ad43e4SBin Meng 		}
123938ad43e4SBin Meng 	} while (!all_edges_found);
124038ad43e4SBin Meng 
124138ad43e4SBin Meng 	/* restore DDR idle state */
124238ad43e4SBin Meng 	dram_init_command(DCMD_PREA(rank));
124338ad43e4SBin Meng 
124438ad43e4SBin Meng 	DPF(D_TRN, "Delay %03X %03X %03X %03X\n",
124538ad43e4SBin Meng 	    delay[0], delay[1], delay[2], delay[3]);
124638ad43e4SBin Meng 
124738ad43e4SBin Meng 	LEAVEFN();
124838ad43e4SBin Meng }
124938ad43e4SBin Meng 
125038ad43e4SBin Meng /*
125138ad43e4SBin Meng  * This function will return a 32 bit mask that will be used to
125238ad43e4SBin Meng  * check for byte lane failures.
125338ad43e4SBin Meng  */
byte_lane_mask(struct mrc_params * mrc_params)125438ad43e4SBin Meng uint32_t byte_lane_mask(struct mrc_params *mrc_params)
125538ad43e4SBin Meng {
125638ad43e4SBin Meng 	uint32_t j;
125738ad43e4SBin Meng 	uint32_t ret_val = 0x00;
125838ad43e4SBin Meng 
125938ad43e4SBin Meng 	/*
126038ad43e4SBin Meng 	 * set ret_val based on NUM_BYTE_LANES such that you will check
126138ad43e4SBin Meng 	 * only BL0 in result
126238ad43e4SBin Meng 	 *
126338ad43e4SBin Meng 	 * (each bit in result represents a byte lane)
126438ad43e4SBin Meng 	 */
126538ad43e4SBin Meng 	for (j = 0; j < MAX_BYTE_LANES; j += NUM_BYTE_LANES)
126638ad43e4SBin Meng 		ret_val |= (1 << ((j / NUM_BYTE_LANES) * NUM_BYTE_LANES));
126738ad43e4SBin Meng 
126838ad43e4SBin Meng 	/*
126938ad43e4SBin Meng 	 * HSD#235037
127038ad43e4SBin Meng 	 * need to adjust the mask for 16-bit mode
127138ad43e4SBin Meng 	 */
127238ad43e4SBin Meng 	if (mrc_params->channel_width == X16)
127338ad43e4SBin Meng 		ret_val |= (ret_val << 2);
127438ad43e4SBin Meng 
127538ad43e4SBin Meng 	return ret_val;
127638ad43e4SBin Meng }
127738ad43e4SBin Meng 
127838ad43e4SBin Meng /*
127938ad43e4SBin Meng  * Check memory executing simple write/read/verify at the specified address.
128038ad43e4SBin Meng  *
128138ad43e4SBin Meng  * Bits in the result indicate failure on specific byte lane.
128238ad43e4SBin Meng  */
check_rw_coarse(struct mrc_params * mrc_params,uint32_t address)128338ad43e4SBin Meng uint32_t check_rw_coarse(struct mrc_params *mrc_params, uint32_t address)
128438ad43e4SBin Meng {
128538ad43e4SBin Meng 	uint32_t result = 0;
128638ad43e4SBin Meng 	uint8_t first_run = 0;
128738ad43e4SBin Meng 
128838ad43e4SBin Meng 	if (mrc_params->hte_setup) {
128938ad43e4SBin Meng 		mrc_params->hte_setup = 0;
129038ad43e4SBin Meng 		first_run = 1;
129138ad43e4SBin Meng 		select_hte();
129238ad43e4SBin Meng 	}
129338ad43e4SBin Meng 
129438ad43e4SBin Meng 	result = hte_basic_write_read(mrc_params, address, first_run,
129538ad43e4SBin Meng 				      WRITE_TRAIN);
129638ad43e4SBin Meng 
129738ad43e4SBin Meng 	DPF(D_TRN, "check_rw_coarse result is %x\n", result);
129838ad43e4SBin Meng 
129938ad43e4SBin Meng 	return result;
130038ad43e4SBin Meng }
130138ad43e4SBin Meng 
130238ad43e4SBin Meng /*
130338ad43e4SBin Meng  * Check memory executing write/read/verify of many data patterns
130438ad43e4SBin Meng  * at the specified address. Bits in the result indicate failure
130538ad43e4SBin Meng  * on specific byte lane.
130638ad43e4SBin Meng  */
check_bls_ex(struct mrc_params * mrc_params,uint32_t address)130738ad43e4SBin Meng uint32_t check_bls_ex(struct mrc_params *mrc_params, uint32_t address)
130838ad43e4SBin Meng {
130938ad43e4SBin Meng 	uint32_t result;
131038ad43e4SBin Meng 	uint8_t first_run = 0;
131138ad43e4SBin Meng 
131238ad43e4SBin Meng 	if (mrc_params->hte_setup) {
131338ad43e4SBin Meng 		mrc_params->hte_setup = 0;
131438ad43e4SBin Meng 		first_run = 1;
131538ad43e4SBin Meng 		select_hte();
131638ad43e4SBin Meng 	}
131738ad43e4SBin Meng 
131838ad43e4SBin Meng 	result = hte_write_stress_bit_lanes(mrc_params, address, first_run);
131938ad43e4SBin Meng 
132038ad43e4SBin Meng 	DPF(D_TRN, "check_bls_ex result is %x\n", result);
132138ad43e4SBin Meng 
132238ad43e4SBin Meng 	return result;
132338ad43e4SBin Meng }
132438ad43e4SBin Meng 
132538ad43e4SBin Meng /*
132638ad43e4SBin Meng  * 32-bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1
132738ad43e4SBin Meng  *
132838ad43e4SBin Meng  * The function takes pointer to previous 32 bit value and
132938ad43e4SBin Meng  * modifies it to next value.
133038ad43e4SBin Meng  */
lfsr32(uint32_t * lfsr_ptr)133138ad43e4SBin Meng void lfsr32(uint32_t *lfsr_ptr)
133238ad43e4SBin Meng {
133338ad43e4SBin Meng 	uint32_t bit;
133438ad43e4SBin Meng 	uint32_t lfsr;
133538ad43e4SBin Meng 	int i;
133638ad43e4SBin Meng 
133738ad43e4SBin Meng 	lfsr = *lfsr_ptr;
133838ad43e4SBin Meng 
133938ad43e4SBin Meng 	for (i = 0; i < 32; i++) {
1340312cc39eSBin Meng 		bit = 1 ^ (lfsr & 1);
1341312cc39eSBin Meng 		bit = bit ^ ((lfsr & 2) >> 1);
1342312cc39eSBin Meng 		bit = bit ^ ((lfsr & 4) >> 2);
1343312cc39eSBin Meng 		bit = bit ^ ((lfsr & 0x400000) >> 22);
134438ad43e4SBin Meng 
134538ad43e4SBin Meng 		lfsr = ((lfsr >> 1) | (bit << 31));
134638ad43e4SBin Meng 	}
134738ad43e4SBin Meng 
134838ad43e4SBin Meng 	*lfsr_ptr = lfsr;
134938ad43e4SBin Meng }
135038ad43e4SBin Meng 
135138ad43e4SBin Meng /* Clear the pointers in a given byte lane in a given channel */
clear_pointers(void)135238ad43e4SBin Meng void clear_pointers(void)
135338ad43e4SBin Meng {
135438ad43e4SBin Meng 	uint8_t channel;
135538ad43e4SBin Meng 	uint8_t bl;
135638ad43e4SBin Meng 
135738ad43e4SBin Meng 	ENTERFN();
135838ad43e4SBin Meng 
135938ad43e4SBin Meng 	for (channel = 0; channel < NUM_CHANNELS; channel++) {
136038ad43e4SBin Meng 		for (bl = 0; bl < NUM_BYTE_LANES; bl++) {
136138ad43e4SBin Meng 			mrc_alt_write_mask(DDRPHY,
1362312cc39eSBin Meng 					   B01PTRCTL1 +
1363312cc39eSBin Meng 					   channel * DDRIODQ_CH_OFFSET +
1364312cc39eSBin Meng 					   (bl >> 1) * DDRIODQ_BL_OFFSET,
1365312cc39eSBin Meng 					   ~(1 << 8), (1 << 8));
136638ad43e4SBin Meng 
136738ad43e4SBin Meng 			mrc_alt_write_mask(DDRPHY,
1368312cc39eSBin Meng 					   B01PTRCTL1 +
1369312cc39eSBin Meng 					   channel * DDRIODQ_CH_OFFSET +
1370312cc39eSBin Meng 					   (bl >> 1) * DDRIODQ_BL_OFFSET,
1371312cc39eSBin Meng 					   (1 << 8), (1 << 8));
137238ad43e4SBin Meng 		}
137338ad43e4SBin Meng 	}
137438ad43e4SBin Meng 
137538ad43e4SBin Meng 	LEAVEFN();
137638ad43e4SBin Meng }
137738ad43e4SBin Meng 
print_timings_internal(uint8_t algo,uint8_t channel,uint8_t rank,uint8_t bl_divisor)137838ad43e4SBin Meng static void print_timings_internal(uint8_t algo, uint8_t channel, uint8_t rank,
137938ad43e4SBin Meng 				   uint8_t bl_divisor)
138038ad43e4SBin Meng {
138138ad43e4SBin Meng 	uint8_t bl;
138238ad43e4SBin Meng 
138338ad43e4SBin Meng 	switch (algo) {
138438ad43e4SBin Meng 	case RCVN:
138538ad43e4SBin Meng 		DPF(D_INFO, "\nRCVN[%02d:%02d]", channel, rank);
138638ad43e4SBin Meng 		break;
138738ad43e4SBin Meng 	case WDQS:
138838ad43e4SBin Meng 		DPF(D_INFO, "\nWDQS[%02d:%02d]", channel, rank);
138938ad43e4SBin Meng 		break;
139038ad43e4SBin Meng 	case WDQX:
139138ad43e4SBin Meng 		DPF(D_INFO, "\nWDQx[%02d:%02d]", channel, rank);
139238ad43e4SBin Meng 		break;
139338ad43e4SBin Meng 	case RDQS:
139438ad43e4SBin Meng 		DPF(D_INFO, "\nRDQS[%02d:%02d]", channel, rank);
139538ad43e4SBin Meng 		break;
139638ad43e4SBin Meng 	case VREF:
139738ad43e4SBin Meng 		DPF(D_INFO, "\nVREF[%02d:%02d]", channel, rank);
139838ad43e4SBin Meng 		break;
139938ad43e4SBin Meng 	case WCMD:
140038ad43e4SBin Meng 		DPF(D_INFO, "\nWCMD[%02d:%02d]", channel, rank);
140138ad43e4SBin Meng 		break;
140238ad43e4SBin Meng 	case WCTL:
140338ad43e4SBin Meng 		DPF(D_INFO, "\nWCTL[%02d:%02d]", channel, rank);
140438ad43e4SBin Meng 		break;
140538ad43e4SBin Meng 	case WCLK:
140638ad43e4SBin Meng 		DPF(D_INFO, "\nWCLK[%02d:%02d]", channel, rank);
140738ad43e4SBin Meng 		break;
140838ad43e4SBin Meng 	default:
140938ad43e4SBin Meng 		break;
141038ad43e4SBin Meng 	}
141138ad43e4SBin Meng 
1412312cc39eSBin Meng 	for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
141338ad43e4SBin Meng 		switch (algo) {
141438ad43e4SBin Meng 		case RCVN:
141538ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_rcvn(channel, rank, bl));
141638ad43e4SBin Meng 			break;
141738ad43e4SBin Meng 		case WDQS:
141838ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_wdqs(channel, rank, bl));
141938ad43e4SBin Meng 			break;
142038ad43e4SBin Meng 		case WDQX:
142138ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_wdq(channel, rank, bl));
142238ad43e4SBin Meng 			break;
142338ad43e4SBin Meng 		case RDQS:
142438ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_rdqs(channel, rank, bl));
142538ad43e4SBin Meng 			break;
142638ad43e4SBin Meng 		case VREF:
142738ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_vref(channel, bl));
142838ad43e4SBin Meng 			break;
142938ad43e4SBin Meng 		case WCMD:
143038ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_wcmd(channel));
143138ad43e4SBin Meng 			break;
143238ad43e4SBin Meng 		case WCTL:
143338ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_wctl(channel, rank));
143438ad43e4SBin Meng 			break;
143538ad43e4SBin Meng 		case WCLK:
143638ad43e4SBin Meng 			DPF(D_INFO, " %03d", get_wclk(channel, rank));
143738ad43e4SBin Meng 			break;
143838ad43e4SBin Meng 		default:
143938ad43e4SBin Meng 			break;
144038ad43e4SBin Meng 		}
144138ad43e4SBin Meng 	}
144238ad43e4SBin Meng }
144338ad43e4SBin Meng 
print_timings(struct mrc_params * mrc_params)144438ad43e4SBin Meng void print_timings(struct mrc_params *mrc_params)
144538ad43e4SBin Meng {
144638ad43e4SBin Meng 	uint8_t algo;
144738ad43e4SBin Meng 	uint8_t channel;
144838ad43e4SBin Meng 	uint8_t rank;
144938ad43e4SBin Meng 	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
145038ad43e4SBin Meng 
145138ad43e4SBin Meng 	DPF(D_INFO, "\n---------------------------");
145238ad43e4SBin Meng 	DPF(D_INFO, "\nALGO[CH:RK] BL0 BL1 BL2 BL3");
145338ad43e4SBin Meng 	DPF(D_INFO, "\n===========================");
145438ad43e4SBin Meng 
145538ad43e4SBin Meng 	for (algo = 0; algo < MAX_ALGOS; algo++) {
145638ad43e4SBin Meng 		for (channel = 0; channel < NUM_CHANNELS; channel++) {
145738ad43e4SBin Meng 			if (mrc_params->channel_enables & (1 << channel)) {
145838ad43e4SBin Meng 				for (rank = 0; rank < NUM_RANKS; rank++) {
145938ad43e4SBin Meng 					if (mrc_params->rank_enables &
146038ad43e4SBin Meng 						(1 << rank)) {
146138ad43e4SBin Meng 						print_timings_internal(algo,
146238ad43e4SBin Meng 							channel, rank,
146338ad43e4SBin Meng 							bl_divisor);
146438ad43e4SBin Meng 					}
146538ad43e4SBin Meng 				}
146638ad43e4SBin Meng 			}
146738ad43e4SBin Meng 		}
146838ad43e4SBin Meng 	}
146938ad43e4SBin Meng 
147038ad43e4SBin Meng 	DPF(D_INFO, "\n---------------------------");
147138ad43e4SBin Meng 	DPF(D_INFO, "\n");
147238ad43e4SBin Meng }
1473