]>
Commit | Line | Data |
---|---|---|
3bb3f266 KS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
a8c13c77 | 3 | * Texas Instruments' K3 DDRSS driver |
3bb3f266 | 4 | * |
a94a4071 | 5 | * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ |
3bb3f266 KS |
6 | */ |
7 | ||
d678a59d | 8 | #include <common.h> |
f861ce90 | 9 | #include <config.h> |
3bb3f266 | 10 | #include <clk.h> |
f861ce90 | 11 | #include <div64.h> |
3bb3f266 | 12 | #include <dm.h> |
a8c13c77 | 13 | #include <dm/device_compat.h> |
f861ce90 | 14 | #include <fdt_support.h> |
a8c13c77 | 15 | #include <ram.h> |
db41d65a | 16 | #include <hang.h> |
f7ae49fc | 17 | #include <log.h> |
3bb3f266 KS |
18 | #include <asm/io.h> |
19 | #include <power-domain.h> | |
20 | #include <wait_bit.h> | |
2ce6dedf | 21 | #include <power/regulator.h> |
3bb3f266 KS |
22 | |
23 | #include "lpddr4_obj_if.h" | |
24 | #include "lpddr4_if.h" | |
25 | #include "lpddr4_structs_if.h" | |
26 | #include "lpddr4_ctl_regs.h" | |
27 | ||
28 | #define SRAM_MAX 512 | |
29 | ||
30 | #define CTRLMMR_DDR4_FSP_CLKCHNG_REQ_OFFS 0x80 | |
31 | #define CTRLMMR_DDR4_FSP_CLKCHNG_ACK_OFFS 0xc0 | |
32 | ||
b4c80f24 | 33 | #define DDRSS_V2A_CTL_REG 0x0020 |
0cf207ec | 34 | #define DDRSS_ECC_CTRL_REG 0x0120 |
9f9b5c1c | 35 | |
f861ce90 DG |
36 | #define DDRSS_ECC_CTRL_REG_ECC_EN BIT(0) |
37 | #define DDRSS_ECC_CTRL_REG_RMW_EN BIT(1) | |
38 | #define DDRSS_ECC_CTRL_REG_ECC_CK BIT(2) | |
39 | #define DDRSS_ECC_CTRL_REG_WR_ALLOC BIT(4) | |
40 | ||
41 | #define DDRSS_ECC_R0_STR_ADDR_REG 0x0130 | |
42 | #define DDRSS_ECC_R0_END_ADDR_REG 0x0134 | |
43 | #define DDRSS_ECC_R1_STR_ADDR_REG 0x0138 | |
44 | #define DDRSS_ECC_R1_END_ADDR_REG 0x013c | |
45 | #define DDRSS_ECC_R2_STR_ADDR_REG 0x0140 | |
46 | #define DDRSS_ECC_R2_END_ADDR_REG 0x0144 | |
47 | #define DDRSS_ECC_1B_ERR_CNT_REG 0x0150 | |
48 | ||
1a99bec0 AG |
49 | #define SINGLE_DDR_SUBSYSTEM 0x1 |
50 | #define MULTI_DDR_SUBSYSTEM 0x2 | |
51 | ||
a48fc5cc AG |
52 | #define MULTI_DDR_CFG0 0x00114100 |
53 | #define MULTI_DDR_CFG1 0x00114104 | |
54 | #define DDR_CFG_LOAD 0x00114110 | |
55 | ||
56 | enum intrlv_gran { | |
57 | GRAN_128B, | |
58 | GRAN_512B, | |
59 | GRAN_2KB, | |
60 | GRAN_4KB, | |
61 | GRAN_16KB, | |
62 | GRAN_32KB, | |
63 | GRAN_512KB, | |
64 | GRAN_1GB, | |
65 | GRAN_1_5GB, | |
66 | GRAN_2GB, | |
67 | GRAN_3GB, | |
68 | GRAN_4GB, | |
69 | GRAN_6GB, | |
70 | GRAN_8GB, | |
71 | GRAN_16GB | |
72 | }; | |
73 | ||
74 | enum intrlv_size { | |
75 | SIZE_0, | |
76 | SIZE_128MB, | |
77 | SIZE_256MB, | |
78 | SIZE_512MB, | |
79 | SIZE_1GB, | |
80 | SIZE_2GB, | |
81 | SIZE_3GB, | |
82 | SIZE_4GB, | |
83 | SIZE_6GB, | |
84 | SIZE_8GB, | |
85 | SIZE_12GB, | |
86 | SIZE_16GB, | |
87 | SIZE_32GB | |
88 | }; | |
89 | ||
90 | struct k3_ddrss_data { | |
91 | u32 flags; | |
92 | }; | |
93 | ||
94 | enum ecc_enable { | |
95 | DISABLE_ALL = 0, | |
96 | ENABLE_0, | |
97 | ENABLE_1, | |
98 | ENABLE_ALL | |
99 | }; | |
100 | ||
101 | enum emif_config { | |
102 | INTERLEAVE_ALL = 0, | |
103 | SEPR0, | |
104 | SEPR1 | |
105 | }; | |
106 | ||
107 | enum emif_active { | |
108 | EMIF_0 = 1, | |
109 | EMIF_1, | |
110 | EMIF_ALL | |
111 | }; | |
112 | ||
113 | struct k3_msmc { | |
114 | enum intrlv_gran gran; | |
115 | enum intrlv_size size; | |
116 | enum ecc_enable enable; | |
117 | enum emif_config config; | |
118 | enum emif_active active; | |
119 | }; | |
120 | ||
f861ce90 DG |
121 | #define K3_DDRSS_MAX_ECC_REGIONS 3 |
122 | ||
123 | struct k3_ddrss_ecc_region { | |
124 | u32 start; | |
125 | u32 range; | |
126 | }; | |
127 | ||
a8c13c77 | 128 | struct k3_ddrss_desc { |
3bb3f266 KS |
129 | struct udevice *dev; |
130 | void __iomem *ddrss_ss_cfg; | |
131 | void __iomem *ddrss_ctrl_mmr; | |
71eb5274 | 132 | void __iomem *ddrss_ctl_cfg; |
3bb3f266 KS |
133 | struct power_domain ddrcfg_pwrdmn; |
134 | struct power_domain ddrdata_pwrdmn; | |
135 | struct clk ddr_clk; | |
136 | struct clk osc_clk; | |
270f7fd2 | 137 | u32 ddr_freq0; |
3bb3f266 KS |
138 | u32 ddr_freq1; |
139 | u32 ddr_freq2; | |
140 | u32 ddr_fhs_cnt; | |
af7c33c1 | 141 | u32 dram_class; |
2ce6dedf | 142 | struct udevice *vtt_supply; |
1a99bec0 AG |
143 | u32 instance; |
144 | lpddr4_obj *driverdt; | |
145 | lpddr4_config config; | |
146 | lpddr4_privatedata pd; | |
f861ce90 DG |
147 | struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REGIONS]; |
148 | u64 ecc_reserved_space; | |
149 | bool ti_ecc_enabled; | |
3bb3f266 KS |
150 | }; |
151 | ||
a8c13c77 DG |
152 | struct reginitdata { |
153 | u32 ctl_regs[LPDDR4_INTR_CTL_REG_COUNT]; | |
154 | u16 ctl_regs_offs[LPDDR4_INTR_CTL_REG_COUNT]; | |
155 | u32 pi_regs[LPDDR4_INTR_PHY_INDEP_REG_COUNT]; | |
156 | u16 pi_regs_offs[LPDDR4_INTR_PHY_INDEP_REG_COUNT]; | |
157 | u32 phy_regs[LPDDR4_INTR_PHY_REG_COUNT]; | |
158 | u16 phy_regs_offs[LPDDR4_INTR_PHY_REG_COUNT]; | |
159 | }; | |
3bb3f266 KS |
160 | |
161 | #define TH_MACRO_EXP(fld, str) (fld##str) | |
162 | ||
163 | #define TH_FLD_MASK(fld) TH_MACRO_EXP(fld, _MASK) | |
164 | #define TH_FLD_SHIFT(fld) TH_MACRO_EXP(fld, _SHIFT) | |
165 | #define TH_FLD_WIDTH(fld) TH_MACRO_EXP(fld, _WIDTH) | |
166 | #define TH_FLD_WOCLR(fld) TH_MACRO_EXP(fld, _WOCLR) | |
167 | #define TH_FLD_WOSET(fld) TH_MACRO_EXP(fld, _WOSET) | |
168 | ||
169 | #define str(s) #s | |
170 | #define xstr(s) str(s) | |
171 | ||
a8c13c77 DG |
172 | #define CTL_SHIFT 11 |
173 | #define PHY_SHIFT 11 | |
174 | #define PI_SHIFT 10 | |
175 | ||
176 | #define DENALI_CTL_0_DRAM_CLASS_DDR4 0xA | |
177 | #define DENALI_CTL_0_DRAM_CLASS_LPDDR4 0xB | |
3bb3f266 KS |
178 | |
179 | #define TH_OFFSET_FROM_REG(REG, SHIFT, offset) do {\ | |
a8c13c77 | 180 | char *i, *pstr = xstr(REG); offset = 0;\ |
3bb3f266 | 181 | for (i = &pstr[SHIFT]; *i != '\0'; ++i) {\ |
a8c13c77 | 182 | offset = offset * 10 + (*i - '0'); } \ |
3bb3f266 KS |
183 | } while (0) |
184 | ||
1a99bec0 | 185 | static u32 k3_lpddr4_read_ddr_type(const lpddr4_privatedata *pd) |
3bb3f266 | 186 | { |
a8c13c77 DG |
187 | u32 status = 0U; |
188 | u32 offset = 0U; | |
189 | u32 regval = 0U; | |
190 | u32 dram_class = 0U; | |
1a99bec0 | 191 | struct k3_ddrss_desc *ddrss = (struct k3_ddrss_desc *)pd->ddr_instance; |
3bb3f266 | 192 | |
a8c13c77 | 193 | TH_OFFSET_FROM_REG(LPDDR4__DRAM_CLASS__REG, CTL_SHIFT, offset); |
1a99bec0 | 194 | status = ddrss->driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); |
a8c13c77 DG |
195 | if (status > 0U) { |
196 | printf("%s: Failed to read DRAM_CLASS\n", __func__); | |
197 | hang(); | |
198 | } | |
199 | ||
200 | dram_class = ((regval & TH_FLD_MASK(LPDDR4__DRAM_CLASS__FLD)) >> | |
201 | TH_FLD_SHIFT(LPDDR4__DRAM_CLASS__FLD)); | |
202 | return dram_class; | |
203 | } | |
204 | ||
1a99bec0 | 205 | static void k3_lpddr4_freq_update(struct k3_ddrss_desc *ddrss) |
a8c13c77 DG |
206 | { |
207 | unsigned int req_type, counter; | |
3bb3f266 KS |
208 | |
209 | for (counter = 0; counter < ddrss->ddr_fhs_cnt; counter++) { | |
210 | if (wait_for_bit_le32(ddrss->ddrss_ctrl_mmr + | |
1a99bec0 | 211 | CTRLMMR_DDR4_FSP_CLKCHNG_REQ_OFFS + ddrss->instance * 0x10, 0x80, |
3bb3f266 KS |
212 | true, 10000, false)) { |
213 | printf("Timeout during frequency handshake\n"); | |
214 | hang(); | |
215 | } | |
216 | ||
217 | req_type = readl(ddrss->ddrss_ctrl_mmr + | |
1a99bec0 | 218 | CTRLMMR_DDR4_FSP_CLKCHNG_REQ_OFFS + ddrss->instance * 0x10) & 0x03; |
3bb3f266 | 219 | |
1a99bec0 AG |
220 | debug("%s: received freq change req: req type = %d, req no. = %d, instance = %d\n", |
221 | __func__, req_type, counter, ddrss->instance); | |
3bb3f266 KS |
222 | |
223 | if (req_type == 1) | |
224 | clk_set_rate(&ddrss->ddr_clk, ddrss->ddr_freq1); | |
225 | else if (req_type == 2) | |
226 | clk_set_rate(&ddrss->ddr_clk, ddrss->ddr_freq2); | |
227 | else if (req_type == 0) | |
270f7fd2 | 228 | clk_set_rate(&ddrss->ddr_clk, ddrss->ddr_freq0); |
3bb3f266 KS |
229 | else |
230 | printf("%s: Invalid freq request type\n", __func__); | |
231 | ||
232 | writel(0x1, ddrss->ddrss_ctrl_mmr + | |
1a99bec0 | 233 | CTRLMMR_DDR4_FSP_CLKCHNG_ACK_OFFS + ddrss->instance * 0x10); |
3bb3f266 | 234 | if (wait_for_bit_le32(ddrss->ddrss_ctrl_mmr + |
1a99bec0 | 235 | CTRLMMR_DDR4_FSP_CLKCHNG_REQ_OFFS + ddrss->instance * 0x10, 0x80, |
3bb3f266 KS |
236 | false, 10, false)) { |
237 | printf("Timeout during frequency handshake\n"); | |
238 | hang(); | |
239 | } | |
240 | writel(0x0, ddrss->ddrss_ctrl_mmr + | |
1a99bec0 | 241 | CTRLMMR_DDR4_FSP_CLKCHNG_ACK_OFFS + ddrss->instance * 0x10); |
3bb3f266 KS |
242 | } |
243 | } | |
244 | ||
1a99bec0 | 245 | static void k3_lpddr4_ack_freq_upd_req(const lpddr4_privatedata *pd) |
a8c13c77 | 246 | { |
1a99bec0 | 247 | struct k3_ddrss_desc *ddrss = (struct k3_ddrss_desc *)pd->ddr_instance; |
a8c13c77 DG |
248 | |
249 | debug("--->>> LPDDR4 Initialization is in progress ... <<<---\n"); | |
250 | ||
af7c33c1 | 251 | switch (ddrss->dram_class) { |
a8c13c77 DG |
252 | case DENALI_CTL_0_DRAM_CLASS_DDR4: |
253 | break; | |
254 | case DENALI_CTL_0_DRAM_CLASS_LPDDR4: | |
1a99bec0 | 255 | k3_lpddr4_freq_update(ddrss); |
a8c13c77 DG |
256 | break; |
257 | default: | |
258 | printf("Unrecognized dram_class cannot update frequency!\n"); | |
259 | } | |
260 | } | |
261 | ||
262 | static int k3_ddrss_init_freq(struct k3_ddrss_desc *ddrss) | |
3bb3f266 | 263 | { |
a8c13c77 | 264 | int ret; |
1a99bec0 | 265 | lpddr4_privatedata *pd = &ddrss->pd; |
a8c13c77 | 266 | |
af7c33c1 | 267 | ddrss->dram_class = k3_lpddr4_read_ddr_type(pd); |
a8c13c77 | 268 | |
af7c33c1 | 269 | switch (ddrss->dram_class) { |
a8c13c77 DG |
270 | case DENALI_CTL_0_DRAM_CLASS_DDR4: |
271 | /* Set to ddr_freq1 from DT for DDR4 */ | |
272 | ret = clk_set_rate(&ddrss->ddr_clk, ddrss->ddr_freq1); | |
273 | break; | |
274 | case DENALI_CTL_0_DRAM_CLASS_LPDDR4: | |
270f7fd2 | 275 | ret = clk_set_rate(&ddrss->ddr_clk, ddrss->ddr_freq0); |
a8c13c77 DG |
276 | break; |
277 | default: | |
278 | ret = -EINVAL; | |
279 | printf("Unrecognized dram_class cannot init frequency!\n"); | |
3bb3f266 | 280 | } |
a8c13c77 DG |
281 | |
282 | if (ret < 0) | |
283 | dev_err(ddrss->dev, "ddr clk init failed: %d\n", ret); | |
284 | else | |
285 | ret = 0; | |
286 | ||
287 | return ret; | |
288 | } | |
289 | ||
290 | static void k3_lpddr4_info_handler(const lpddr4_privatedata *pd, | |
291 | lpddr4_infotype infotype) | |
292 | { | |
293 | if (infotype == LPDDR4_DRV_SOC_PLL_UPDATE) | |
1a99bec0 | 294 | k3_lpddr4_ack_freq_upd_req(pd); |
3bb3f266 KS |
295 | } |
296 | ||
a8c13c77 | 297 | static int k3_ddrss_power_on(struct k3_ddrss_desc *ddrss) |
3bb3f266 KS |
298 | { |
299 | int ret; | |
300 | ||
301 | debug("%s(ddrss=%p)\n", __func__, ddrss); | |
302 | ||
303 | ret = power_domain_on(&ddrss->ddrcfg_pwrdmn); | |
304 | if (ret) { | |
305 | dev_err(ddrss->dev, "power_domain_on() failed: %d\n", ret); | |
306 | return ret; | |
307 | } | |
308 | ||
309 | ret = power_domain_on(&ddrss->ddrdata_pwrdmn); | |
310 | if (ret) { | |
311 | dev_err(ddrss->dev, "power_domain_on() failed: %d\n", ret); | |
312 | return ret; | |
313 | } | |
314 | ||
2ce6dedf LV |
315 | ret = device_get_supply_regulator(ddrss->dev, "vtt-supply", |
316 | &ddrss->vtt_supply); | |
317 | if (ret) { | |
318 | dev_dbg(ddrss->dev, "vtt-supply not found.\n"); | |
319 | } else { | |
320 | ret = regulator_set_value(ddrss->vtt_supply, 3300000); | |
321 | if (ret) | |
322 | return ret; | |
323 | dev_dbg(ddrss->dev, "VTT regulator enabled, volt = %d\n", | |
324 | regulator_get_value(ddrss->vtt_supply)); | |
325 | } | |
326 | ||
3bb3f266 KS |
327 | return 0; |
328 | } | |
329 | ||
a8c13c77 | 330 | static int k3_ddrss_ofdata_to_priv(struct udevice *dev) |
3bb3f266 | 331 | { |
a8c13c77 | 332 | struct k3_ddrss_desc *ddrss = dev_get_priv(dev); |
1a99bec0 | 333 | struct k3_ddrss_data *ddrss_data = (struct k3_ddrss_data *)dev_get_driver_data(dev); |
5fecea17 | 334 | void *reg; |
3bb3f266 KS |
335 | int ret; |
336 | ||
337 | debug("%s(dev=%p)\n", __func__, dev); | |
338 | ||
5fecea17 MS |
339 | reg = dev_read_addr_name_ptr(dev, "cfg"); |
340 | if (!reg) { | |
3bb3f266 KS |
341 | dev_err(dev, "No reg property for DDRSS wrapper logic\n"); |
342 | return -EINVAL; | |
343 | } | |
5fecea17 | 344 | ddrss->ddrss_ctl_cfg = reg; |
3bb3f266 | 345 | |
5fecea17 MS |
346 | reg = dev_read_addr_name_ptr(dev, "ctrl_mmr_lp4"); |
347 | if (!reg) { | |
3bb3f266 KS |
348 | dev_err(dev, "No reg property for CTRL MMR\n"); |
349 | return -EINVAL; | |
350 | } | |
5fecea17 | 351 | ddrss->ddrss_ctrl_mmr = reg; |
3bb3f266 | 352 | |
5fecea17 MS |
353 | reg = dev_read_addr_name_ptr(dev, "ss_cfg"); |
354 | if (!reg) | |
f861ce90 | 355 | dev_dbg(dev, "No reg property for SS Config region, but this is optional so continuing.\n"); |
5fecea17 | 356 | ddrss->ddrss_ss_cfg = reg; |
f861ce90 | 357 | |
3bb3f266 KS |
358 | ret = power_domain_get_by_index(dev, &ddrss->ddrcfg_pwrdmn, 0); |
359 | if (ret) { | |
360 | dev_err(dev, "power_domain_get() failed: %d\n", ret); | |
361 | return ret; | |
362 | } | |
363 | ||
364 | ret = power_domain_get_by_index(dev, &ddrss->ddrdata_pwrdmn, 1); | |
365 | if (ret) { | |
366 | dev_err(dev, "power_domain_get() failed: %d\n", ret); | |
367 | return ret; | |
368 | } | |
369 | ||
370 | ret = clk_get_by_index(dev, 0, &ddrss->ddr_clk); | |
371 | if (ret) | |
372 | dev_err(dev, "clk get failed%d\n", ret); | |
373 | ||
374 | ret = clk_get_by_index(dev, 1, &ddrss->osc_clk); | |
375 | if (ret) | |
376 | dev_err(dev, "clk get failed for osc clk %d\n", ret); | |
377 | ||
1a99bec0 AG |
378 | /* Reading instance number for multi ddr subystems */ |
379 | if (ddrss_data->flags & MULTI_DDR_SUBSYSTEM) { | |
380 | ret = dev_read_u32(dev, "instance", &ddrss->instance); | |
381 | if (ret) { | |
382 | dev_err(dev, "missing instance property"); | |
383 | return -EINVAL; | |
384 | } | |
385 | } else { | |
386 | ddrss->instance = 0; | |
387 | } | |
388 | ||
270f7fd2 DG |
389 | ret = dev_read_u32(dev, "ti,ddr-freq0", &ddrss->ddr_freq0); |
390 | if (ret) { | |
391 | ddrss->ddr_freq0 = clk_get_rate(&ddrss->osc_clk); | |
392 | dev_dbg(dev, | |
393 | "ddr freq0 not populated, using bypass frequency.\n"); | |
394 | } | |
395 | ||
3bb3f266 KS |
396 | ret = dev_read_u32(dev, "ti,ddr-freq1", &ddrss->ddr_freq1); |
397 | if (ret) | |
398 | dev_err(dev, "ddr freq1 not populated %d\n", ret); | |
399 | ||
400 | ret = dev_read_u32(dev, "ti,ddr-freq2", &ddrss->ddr_freq2); | |
401 | if (ret) | |
402 | dev_err(dev, "ddr freq2 not populated %d\n", ret); | |
403 | ||
404 | ret = dev_read_u32(dev, "ti,ddr-fhs-cnt", &ddrss->ddr_fhs_cnt); | |
405 | if (ret) | |
406 | dev_err(dev, "ddr fhs cnt not populated %d\n", ret); | |
407 | ||
f861ce90 DG |
408 | ddrss->ti_ecc_enabled = dev_read_bool(dev, "ti,ecc-enable"); |
409 | ||
3bb3f266 KS |
410 | return ret; |
411 | } | |
412 | ||
1a99bec0 | 413 | void k3_lpddr4_probe(struct k3_ddrss_desc *ddrss) |
3bb3f266 | 414 | { |
a8c13c77 DG |
415 | u32 status = 0U; |
416 | u16 configsize = 0U; | |
1a99bec0 | 417 | lpddr4_config *config = &ddrss->config; |
3bb3f266 | 418 | |
1a99bec0 | 419 | status = ddrss->driverdt->probe(config, &configsize); |
3bb3f266 KS |
420 | |
421 | if ((status != 0) || (configsize != sizeof(lpddr4_privatedata)) | |
422 | || (configsize > SRAM_MAX)) { | |
a8c13c77 | 423 | printf("%s: FAIL\n", __func__); |
3bb3f266 KS |
424 | hang(); |
425 | } else { | |
a8c13c77 | 426 | debug("%s: PASS\n", __func__); |
3bb3f266 KS |
427 | } |
428 | } | |
429 | ||
1a99bec0 | 430 | void k3_lpddr4_init(struct k3_ddrss_desc *ddrss) |
3bb3f266 | 431 | { |
a8c13c77 | 432 | u32 status = 0U; |
1a99bec0 AG |
433 | lpddr4_config *config = &ddrss->config; |
434 | lpddr4_obj *driverdt = ddrss->driverdt; | |
435 | lpddr4_privatedata *pd = &ddrss->pd; | |
3bb3f266 | 436 | |
1a99bec0 | 437 | if ((sizeof(*pd) != sizeof(lpddr4_privatedata)) || (sizeof(*pd) > SRAM_MAX)) { |
a8c13c77 | 438 | printf("%s: FAIL\n", __func__); |
3bb3f266 KS |
439 | hang(); |
440 | } | |
441 | ||
71eb5274 | 442 | config->ctlbase = (struct lpddr4_ctlregs_s *)ddrss->ddrss_ctl_cfg; |
1a99bec0 AG |
443 | config->infohandler = (lpddr4_infocallback) k3_lpddr4_info_handler; |
444 | ||
445 | status = driverdt->init(pd, config); | |
3bb3f266 | 446 | |
1a99bec0 AG |
447 | /* linking ddr instance to lpddr4 */ |
448 | pd->ddr_instance = (void *)ddrss; | |
3bb3f266 KS |
449 | |
450 | if ((status > 0U) || | |
1a99bec0 AG |
451 | (pd->ctlbase != (struct lpddr4_ctlregs_s *)config->ctlbase) || |
452 | (pd->ctlinterrupthandler != config->ctlinterrupthandler) || | |
453 | (pd->phyindepinterrupthandler != config->phyindepinterrupthandler)) { | |
a8c13c77 | 454 | printf("%s: FAIL\n", __func__); |
3bb3f266 KS |
455 | hang(); |
456 | } else { | |
a8c13c77 | 457 | debug("%s: PASS\n", __func__); |
3bb3f266 KS |
458 | } |
459 | } | |
460 | ||
1a99bec0 AG |
461 | void populate_data_array_from_dt(struct k3_ddrss_desc *ddrss, |
462 | struct reginitdata *reginit_data) | |
3bb3f266 KS |
463 | { |
464 | int ret, i; | |
465 | ||
466 | ret = dev_read_u32_array(ddrss->dev, "ti,ctl-data", | |
a8c13c77 DG |
467 | (u32 *)reginit_data->ctl_regs, |
468 | LPDDR4_INTR_CTL_REG_COUNT); | |
3bb3f266 | 469 | if (ret) |
a8c13c77 | 470 | printf("Error reading ctrl data %d\n", ret); |
3bb3f266 | 471 | |
a8c13c77 DG |
472 | for (i = 0; i < LPDDR4_INTR_CTL_REG_COUNT; i++) |
473 | reginit_data->ctl_regs_offs[i] = i; | |
3bb3f266 KS |
474 | |
475 | ret = dev_read_u32_array(ddrss->dev, "ti,pi-data", | |
a8c13c77 DG |
476 | (u32 *)reginit_data->pi_regs, |
477 | LPDDR4_INTR_PHY_INDEP_REG_COUNT); | |
3bb3f266 KS |
478 | if (ret) |
479 | printf("Error reading PI data\n"); | |
480 | ||
a8c13c77 DG |
481 | for (i = 0; i < LPDDR4_INTR_PHY_INDEP_REG_COUNT; i++) |
482 | reginit_data->pi_regs_offs[i] = i; | |
3bb3f266 KS |
483 | |
484 | ret = dev_read_u32_array(ddrss->dev, "ti,phy-data", | |
a8c13c77 DG |
485 | (u32 *)reginit_data->phy_regs, |
486 | LPDDR4_INTR_PHY_REG_COUNT); | |
3bb3f266 | 487 | if (ret) |
a8c13c77 | 488 | printf("Error reading PHY data %d\n", ret); |
3bb3f266 | 489 | |
a8c13c77 DG |
490 | for (i = 0; i < LPDDR4_INTR_PHY_REG_COUNT; i++) |
491 | reginit_data->phy_regs_offs[i] = i; | |
3bb3f266 KS |
492 | } |
493 | ||
1a99bec0 | 494 | void k3_lpddr4_hardware_reg_init(struct k3_ddrss_desc *ddrss) |
3bb3f266 | 495 | { |
a8c13c77 DG |
496 | u32 status = 0U; |
497 | struct reginitdata reginitdata; | |
1a99bec0 AG |
498 | lpddr4_obj *driverdt = ddrss->driverdt; |
499 | lpddr4_privatedata *pd = &ddrss->pd; | |
3bb3f266 | 500 | |
1a99bec0 | 501 | populate_data_array_from_dt(ddrss, ®initdata); |
3bb3f266 | 502 | |
1a99bec0 | 503 | status = driverdt->writectlconfig(pd, reginitdata.ctl_regs, |
a8c13c77 DG |
504 | reginitdata.ctl_regs_offs, |
505 | LPDDR4_INTR_CTL_REG_COUNT); | |
506 | if (!status) | |
1a99bec0 | 507 | status = driverdt->writephyindepconfig(pd, reginitdata.pi_regs, |
a8c13c77 DG |
508 | reginitdata.pi_regs_offs, |
509 | LPDDR4_INTR_PHY_INDEP_REG_COUNT); | |
510 | if (!status) | |
1a99bec0 | 511 | status = driverdt->writephyconfig(pd, reginitdata.phy_regs, |
a8c13c77 DG |
512 | reginitdata.phy_regs_offs, |
513 | LPDDR4_INTR_PHY_REG_COUNT); | |
3bb3f266 | 514 | if (status) { |
a8c13c77 | 515 | printf("%s: FAIL\n", __func__); |
3bb3f266 KS |
516 | hang(); |
517 | } | |
3bb3f266 KS |
518 | } |
519 | ||
1a99bec0 | 520 | void k3_lpddr4_start(struct k3_ddrss_desc *ddrss) |
3bb3f266 | 521 | { |
a8c13c77 DG |
522 | u32 status = 0U; |
523 | u32 regval = 0U; | |
524 | u32 offset = 0U; | |
1a99bec0 AG |
525 | lpddr4_obj *driverdt = ddrss->driverdt; |
526 | lpddr4_privatedata *pd = &ddrss->pd; | |
3bb3f266 KS |
527 | |
528 | TH_OFFSET_FROM_REG(LPDDR4__START__REG, CTL_SHIFT, offset); | |
529 | ||
1a99bec0 | 530 | status = driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); |
3bb3f266 | 531 | if ((status > 0U) || ((regval & TH_FLD_MASK(LPDDR4__START__FLD)) != 0U)) { |
a8c13c77 | 532 | printf("%s: Pre start FAIL\n", __func__); |
3bb3f266 KS |
533 | hang(); |
534 | } | |
535 | ||
1a99bec0 | 536 | status = driverdt->start(pd); |
3bb3f266 | 537 | if (status > 0U) { |
a8c13c77 | 538 | printf("%s: FAIL\n", __func__); |
3bb3f266 KS |
539 | hang(); |
540 | } | |
541 | ||
1a99bec0 | 542 | status = driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); |
3bb3f266 | 543 | if ((status > 0U) || ((regval & TH_FLD_MASK(LPDDR4__START__FLD)) != 1U)) { |
a8c13c77 | 544 | printf("%s: Post start FAIL\n", __func__); |
3bb3f266 KS |
545 | hang(); |
546 | } else { | |
a8c13c77 | 547 | debug("%s: Post start PASS\n", __func__); |
3bb3f266 KS |
548 | } |
549 | } | |
550 | ||
f861ce90 DG |
551 | static void k3_ddrss_set_ecc_range_r0(u32 base, u32 start_address, u32 size) |
552 | { | |
553 | writel((start_address) >> 16, base + DDRSS_ECC_R0_STR_ADDR_REG); | |
554 | writel((start_address + size - 1) >> 16, base + DDRSS_ECC_R0_END_ADDR_REG); | |
555 | } | |
556 | ||
557 | static void k3_ddrss_preload_ecc_mem_region(u32 *addr, u32 size, u32 word) | |
558 | { | |
559 | int i; | |
560 | ||
561 | printf("ECC is enabled, priming DDR which will take several seconds.\n"); | |
562 | ||
563 | for (i = 0; i < (size / 4); i++) | |
564 | addr[i] = word; | |
565 | } | |
566 | ||
567 | static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) | |
568 | { | |
569 | fdtdec_setup_mem_size_base_lowest(); | |
570 | ||
571 | ddrss->ecc_reserved_space = gd->ram_size; | |
572 | do_div(ddrss->ecc_reserved_space, 9); | |
573 | ||
574 | /* Round to clean number */ | |
575 | ddrss->ecc_reserved_space = 1ull << (fls(ddrss->ecc_reserved_space)); | |
576 | } | |
577 | ||
578 | static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) | |
579 | { | |
580 | u32 ecc_region_start = ddrss->ecc_regions[0].start; | |
581 | u32 ecc_range = ddrss->ecc_regions[0].range; | |
582 | u32 base = (u32)ddrss->ddrss_ss_cfg; | |
583 | u32 val; | |
584 | ||
585 | /* Only Program region 0 which covers full ddr space */ | |
586 | k3_ddrss_set_ecc_range_r0(base, ecc_region_start - gd->ram_base, ecc_range); | |
587 | ||
588 | /* Enable ECC, RMW, WR_ALLOC */ | |
589 | writel(DDRSS_ECC_CTRL_REG_ECC_EN | DDRSS_ECC_CTRL_REG_RMW_EN | | |
590 | DDRSS_ECC_CTRL_REG_WR_ALLOC, base + DDRSS_ECC_CTRL_REG); | |
591 | ||
592 | /* Preload ECC Mem region with 0's */ | |
593 | k3_ddrss_preload_ecc_mem_region((u32 *)ecc_region_start, ecc_range, | |
594 | 0x00000000); | |
595 | ||
596 | /* Clear Error Count Register */ | |
597 | writel(0x1, base + DDRSS_ECC_1B_ERR_CNT_REG); | |
598 | ||
599 | /* Enable ECC Check */ | |
600 | val = readl(base + DDRSS_ECC_CTRL_REG); | |
601 | val |= DDRSS_ECC_CTRL_REG_ECC_CK; | |
602 | writel(val, base + DDRSS_ECC_CTRL_REG); | |
603 | } | |
604 | ||
a8c13c77 | 605 | static int k3_ddrss_probe(struct udevice *dev) |
3bb3f266 KS |
606 | { |
607 | int ret; | |
1a99bec0 | 608 | struct k3_ddrss_desc *ddrss = dev_get_priv(dev); |
3bb3f266 KS |
609 | |
610 | debug("%s(dev=%p)\n", __func__, dev); | |
611 | ||
a8c13c77 | 612 | ret = k3_ddrss_ofdata_to_priv(dev); |
3bb3f266 KS |
613 | if (ret) |
614 | return ret; | |
615 | ||
616 | ddrss->dev = dev; | |
a8c13c77 | 617 | ret = k3_ddrss_power_on(ddrss); |
3bb3f266 KS |
618 | if (ret) |
619 | return ret; | |
620 | ||
9f9b5c1c | 621 | #ifdef CONFIG_K3_AM64_DDRSS |
b4c80f24 DR |
622 | /* AM64x supports only up to 2 GB SDRAM */ |
623 | writel(0x000001EF, ddrss->ddrss_ss_cfg + DDRSS_V2A_CTL_REG); | |
9f9b5c1c DG |
624 | writel(0x0, ddrss->ddrss_ss_cfg + DDRSS_ECC_CTRL_REG); |
625 | #endif | |
626 | ||
1a99bec0 AG |
627 | ddrss->driverdt = lpddr4_getinstance(); |
628 | ||
629 | k3_lpddr4_probe(ddrss); | |
630 | k3_lpddr4_init(ddrss); | |
631 | k3_lpddr4_hardware_reg_init(ddrss); | |
a8c13c77 DG |
632 | |
633 | ret = k3_ddrss_init_freq(ddrss); | |
634 | if (ret) | |
635 | return ret; | |
636 | ||
1a99bec0 | 637 | k3_lpddr4_start(ddrss); |
3bb3f266 | 638 | |
f861ce90 DG |
639 | if (ddrss->ti_ecc_enabled) { |
640 | if (!ddrss->ddrss_ss_cfg) { | |
641 | printf("%s: ss_cfg is required if ecc is enabled but not provided.", | |
642 | __func__); | |
643 | return -EINVAL; | |
644 | } | |
645 | ||
646 | k3_ddrss_lpddr4_ecc_calc_reserved_mem(ddrss); | |
647 | ||
648 | /* Always configure one region that covers full DDR space */ | |
649 | ddrss->ecc_regions[0].start = gd->ram_base; | |
650 | ddrss->ecc_regions[0].range = gd->ram_size - ddrss->ecc_reserved_space; | |
651 | k3_ddrss_lpddr4_ecc_init(ddrss); | |
652 | } | |
653 | ||
3bb3f266 KS |
654 | return ret; |
655 | } | |
656 | ||
f861ce90 DG |
657 | int k3_ddrss_ddr_fdt_fixup(struct udevice *dev, void *blob, struct bd_info *bd) |
658 | { | |
659 | struct k3_ddrss_desc *ddrss = dev_get_priv(dev); | |
660 | u64 start[CONFIG_NR_DRAM_BANKS]; | |
661 | u64 size[CONFIG_NR_DRAM_BANKS]; | |
662 | int bank; | |
663 | ||
664 | if (ddrss->ecc_reserved_space == 0) | |
665 | return 0; | |
666 | ||
667 | for (bank = CONFIG_NR_DRAM_BANKS - 1; bank >= 0; bank--) { | |
668 | if (ddrss->ecc_reserved_space > bd->bi_dram[bank].size) { | |
669 | ddrss->ecc_reserved_space -= bd->bi_dram[bank].size; | |
670 | bd->bi_dram[bank].size = 0; | |
671 | } else { | |
672 | bd->bi_dram[bank].size -= ddrss->ecc_reserved_space; | |
673 | break; | |
674 | } | |
675 | } | |
676 | ||
677 | for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { | |
678 | start[bank] = bd->bi_dram[bank].start; | |
679 | size[bank] = bd->bi_dram[bank].size; | |
680 | } | |
681 | ||
682 | return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS); | |
683 | } | |
684 | ||
a8c13c77 | 685 | static int k3_ddrss_get_info(struct udevice *dev, struct ram_info *info) |
3bb3f266 KS |
686 | { |
687 | return 0; | |
688 | } | |
689 | ||
a8c13c77 DG |
690 | static struct ram_ops k3_ddrss_ops = { |
691 | .get_info = k3_ddrss_get_info, | |
3bb3f266 KS |
692 | }; |
693 | ||
1a99bec0 AG |
694 | static const struct k3_ddrss_data k3_data = { |
695 | .flags = SINGLE_DDR_SUBSYSTEM, | |
696 | }; | |
697 | ||
698 | static const struct k3_ddrss_data j721s2_data = { | |
699 | .flags = MULTI_DDR_SUBSYSTEM, | |
700 | }; | |
701 | ||
a8c13c77 | 702 | static const struct udevice_id k3_ddrss_ids[] = { |
f54febe1 | 703 | {.compatible = "ti,am62a-ddrss", .data = (ulong)&k3_data, }, |
1a99bec0 AG |
704 | {.compatible = "ti,am64-ddrss", .data = (ulong)&k3_data, }, |
705 | {.compatible = "ti,j721e-ddrss", .data = (ulong)&k3_data, }, | |
706 | {.compatible = "ti,j721s2-ddrss", .data = (ulong)&j721s2_data, }, | |
3bb3f266 KS |
707 | {} |
708 | }; | |
709 | ||
a8c13c77 DG |
710 | U_BOOT_DRIVER(k3_ddrss) = { |
711 | .name = "k3_ddrss", | |
712 | .id = UCLASS_RAM, | |
713 | .of_match = k3_ddrss_ids, | |
714 | .ops = &k3_ddrss_ops, | |
715 | .probe = k3_ddrss_probe, | |
716 | .priv_auto = sizeof(struct k3_ddrss_desc), | |
3bb3f266 | 717 | }; |
a48fc5cc AG |
718 | |
719 | static int k3_msmc_set_config(struct k3_msmc *msmc) | |
720 | { | |
721 | u32 ddr_cfg0 = 0; | |
722 | u32 ddr_cfg1 = 0; | |
723 | ||
724 | ddr_cfg0 |= msmc->gran << 24; | |
725 | ddr_cfg0 |= msmc->size << 16; | |
726 | /* heartbeat_per, bit[4:0] setting to 3 is advisable */ | |
727 | ddr_cfg0 |= 3; | |
728 | ||
729 | /* Program MULTI_DDR_CFG0 */ | |
730 | writel(ddr_cfg0, MULTI_DDR_CFG0); | |
731 | ||
732 | ddr_cfg1 |= msmc->enable << 16; | |
733 | ddr_cfg1 |= msmc->config << 8; | |
734 | ddr_cfg1 |= msmc->active; | |
735 | ||
736 | /* Program MULTI_DDR_CFG1 */ | |
737 | writel(ddr_cfg1, MULTI_DDR_CFG1); | |
738 | ||
739 | /* Program DDR_CFG_LOAD */ | |
740 | writel(0x60000000, DDR_CFG_LOAD); | |
741 | ||
742 | return 0; | |
743 | } | |
744 | ||
745 | static int k3_msmc_probe(struct udevice *dev) | |
746 | { | |
747 | struct k3_msmc *msmc = dev_get_priv(dev); | |
748 | int ret = 0; | |
749 | ||
750 | /* Read the granular size from DT */ | |
751 | ret = dev_read_u32(dev, "intrlv-gran", &msmc->gran); | |
752 | if (ret) { | |
753 | dev_err(dev, "missing intrlv-gran property"); | |
754 | return -EINVAL; | |
755 | } | |
756 | ||
757 | /* Read the interleave region from DT */ | |
758 | ret = dev_read_u32(dev, "intrlv-size", &msmc->size); | |
759 | if (ret) { | |
760 | dev_err(dev, "missing intrlv-size property"); | |
761 | return -EINVAL; | |
762 | } | |
763 | ||
764 | /* Read ECC enable config */ | |
765 | ret = dev_read_u32(dev, "ecc-enable", &msmc->enable); | |
766 | if (ret) { | |
767 | dev_err(dev, "missing ecc-enable property"); | |
768 | return -EINVAL; | |
769 | } | |
770 | ||
771 | /* Read EMIF configuration */ | |
772 | ret = dev_read_u32(dev, "emif-config", &msmc->config); | |
773 | if (ret) { | |
774 | dev_err(dev, "missing emif-config property"); | |
775 | return -EINVAL; | |
776 | } | |
777 | ||
778 | /* Read EMIF active */ | |
779 | ret = dev_read_u32(dev, "emif-active", &msmc->active); | |
780 | if (ret) { | |
781 | dev_err(dev, "missing emif-active property"); | |
782 | return -EINVAL; | |
783 | } | |
784 | ||
785 | ret = k3_msmc_set_config(msmc); | |
786 | if (ret) { | |
787 | dev_err(dev, "error setting msmc config"); | |
788 | return -EINVAL; | |
789 | } | |
790 | ||
791 | return 0; | |
792 | } | |
793 | ||
794 | static const struct udevice_id k3_msmc_ids[] = { | |
795 | { .compatible = "ti,j721s2-msmc"}, | |
796 | {} | |
797 | }; | |
798 | ||
799 | U_BOOT_DRIVER(k3_msmc) = { | |
800 | .name = "k3_msmc", | |
801 | .of_match = k3_msmc_ids, | |
802 | .id = UCLASS_MISC, | |
803 | .probe = k3_msmc_probe, | |
804 | .priv_auto = sizeof(struct k3_msmc), | |
805 | .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, | |
806 | }; |