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