xref: /rk3399_rockchip-uboot/arch/x86/cpu/quark/mrc_util.c (revision 312cc39e270170b37a992b2d57773c828dcb9c6b)
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