1 /*
2  * Mem setup common file for different types of DDR present on Exynos boards.
3  *
4  * Copyright (C) 2012 Samsung Electronics
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 #include <common.h>
10 #include <asm/arch/spl.h>
11 
12 #include "clock_init.h"
13 #include "common_setup.h"
14 #include "exynos5_setup.h"
15 
16 #define ZQ_INIT_TIMEOUT	10000
17 
18 int dmc_config_zq(struct mem_timings *mem, uint32_t *phy0_con16,
19 			uint32_t *phy1_con16, uint32_t *phy0_con17,
20 			uint32_t *phy1_con17)
21 {
22 	unsigned long val = 0;
23 	int i;
24 
25 	/*
26 	 * ZQ Calibration:
27 	 * Select Driver Strength,
28 	 * long calibration for manual calibration
29 	 */
30 	val = PHY_CON16_RESET_VAL;
31 	val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT;
32 	val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT;
33 	val |= ZQ_CLK_DIV_EN;
34 	writel(val, phy0_con16);
35 	writel(val, phy1_con16);
36 
37 	/* Disable termination */
38 	if (mem->zq_mode_noterm)
39 		val |= PHY_CON16_ZQ_MODE_NOTERM_MASK;
40 	writel(val, phy0_con16);
41 	writel(val, phy1_con16);
42 
43 	/* ZQ_MANUAL_START: Enable */
44 	val |= ZQ_MANUAL_STR;
45 	writel(val, phy0_con16);
46 	writel(val, phy1_con16);
47 
48 	/* ZQ_MANUAL_START: Disable */
49 	val &= ~ZQ_MANUAL_STR;
50 
51 	/*
52 	 * Since we are manaully calibrating the ZQ values,
53 	 * we are looping for the ZQ_init to complete.
54 	 */
55 	i = ZQ_INIT_TIMEOUT;
56 	while ((readl(phy0_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
57 		sdelay(100);
58 		i--;
59 	}
60 	if (!i)
61 		return -1;
62 	writel(val, phy0_con16);
63 
64 	i = ZQ_INIT_TIMEOUT;
65 	while ((readl(phy1_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
66 		sdelay(100);
67 		i--;
68 	}
69 	if (!i)
70 		return -1;
71 	writel(val, phy1_con16);
72 
73 	return 0;
74 }
75 
76 void update_reset_dll(uint32_t *phycontrol0, enum ddr_mode mode)
77 {
78 	unsigned long val;
79 
80 	if (mode == DDR_MODE_DDR3) {
81 		val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE;
82 		writel(val, phycontrol0);
83 	}
84 
85 	/* Update DLL Information: Force DLL Resyncronization */
86 	val = readl(phycontrol0);
87 	val |= FP_RSYNC;
88 	writel(val, phycontrol0);
89 
90 	/* Reset Force DLL Resyncronization */
91 	val = readl(phycontrol0);
92 	val &= ~FP_RSYNC;
93 	writel(val, phycontrol0);
94 }
95 
96 void dmc_config_mrs(struct mem_timings *mem, uint32_t *directcmd)
97 {
98 	int channel, chip;
99 
100 	for (channel = 0; channel < mem->dmc_channels; channel++) {
101 		unsigned long mask;
102 
103 		mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
104 		for (chip = 0; chip < mem->chips_to_configure; chip++) {
105 			int i;
106 
107 			mask |= chip << DIRECT_CMD_CHIP_SHIFT;
108 
109 			/* Sending NOP command */
110 			writel(DIRECT_CMD_NOP | mask, directcmd);
111 
112 			/*
113 			 * TODO(alim.akhtar@samsung.com): Do we need these
114 			 * delays? This one and the next were not there for
115 			 * DDR3.
116 			 */
117 			sdelay(0x10000);
118 
119 			/* Sending EMRS/MRS commands */
120 			for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) {
121 				writel(mem->direct_cmd_msr[i] | mask,
122 				       directcmd);
123 				sdelay(0x10000);
124 			}
125 
126 			if (mem->send_zq_init) {
127 				/* Sending ZQINIT command */
128 				writel(DIRECT_CMD_ZQINIT | mask,
129 				       directcmd);
130 
131 				sdelay(10000);
132 			}
133 		}
134 	}
135 }
136 
137 void dmc_config_prech(struct mem_timings *mem, uint32_t *directcmd)
138 {
139 	int channel, chip;
140 
141 	for (channel = 0; channel < mem->dmc_channels; channel++) {
142 		unsigned long mask;
143 
144 		mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
145 		for (chip = 0; chip < mem->chips_per_channel; chip++) {
146 			mask |= chip << DIRECT_CMD_CHIP_SHIFT;
147 
148 			/* PALL (all banks precharge) CMD */
149 			writel(DIRECT_CMD_PALL | mask, directcmd);
150 			sdelay(0x10000);
151 		}
152 	}
153 }
154 
155 void mem_ctrl_init(int reset)
156 {
157 	struct spl_machine_param *param = spl_get_machine_params();
158 	struct mem_timings *mem;
159 	int ret;
160 
161 	mem = clock_get_mem_timings();
162 
163 	/* If there are any other memory variant, add their init call below */
164 	if (param->mem_type == DDR_MODE_DDR3) {
165 		ret = ddr3_mem_ctrl_init(mem, reset);
166 		if (ret) {
167 			/* will hang if failed to init memory control */
168 			while (1)
169 				;
170 		}
171 	} else {
172 		/* will hang if unknow memory type  */
173 		while (1)
174 			;
175 	}
176 }
177