1 // SPDX-License-Identifier: BSD-3-Clause
2 /*
3 * Cadence DDR Driver
4 *
5 * Copyright (C) 2012-2026 Cadence Design Systems, Inc.
6 * Copyright (C) 2018-2026 Texas Instruments Incorporated - https://www.ti.com/
7 */
8
9 #include <errno.h>
10
11 #include <common/bl_common.h>
12 #include <common/debug.h>
13
14 #include "cps_drv_lpddr4.h"
15 #include "lpddr4.h"
16 #include "lpddr4_if.h"
17 #include "lpddr4_structs_if.h"
18
lpddr4_pollctlirq(const ti_lpddr4_privatedata * pd,ti_lpddr4_intr_ctlinterrupt irqbit,uint32_t delay)19 static uint32_t lpddr4_pollctlirq(const ti_lpddr4_privatedata *pd,
20 ti_lpddr4_intr_ctlinterrupt irqbit,
21 uint32_t delay)
22 {
23 uint32_t result = 0U;
24 uint32_t timeout = 0U;
25 bool irqstatus = false;
26
27 do {
28 if (++timeout == delay) {
29 result = (uint32_t)EIO;
30 break;
31 }
32 result = ti_lpddr4_checkctlinterrupt(pd, irqbit, &irqstatus);
33 } while ((irqstatus == false) && (result == 0U));
34
35 return result;
36 }
37
lpddr4_pollphyindepirq(const ti_lpddr4_privatedata * pd,ti_lpddr4_intr_phyindepinterrupt irqbit,uint32_t delay)38 static uint32_t lpddr4_pollphyindepirq(const ti_lpddr4_privatedata *pd,
39 ti_lpddr4_intr_phyindepinterrupt irqbit,
40 uint32_t delay)
41 {
42 uint32_t result = 0U;
43 uint32_t timeout = 0U;
44 bool irqstatus = false;
45
46 do {
47 if (++timeout == delay) {
48 result = (uint32_t)EIO;
49 break;
50 }
51 result = ti_lpddr4_checkphyindepinterrupt(pd, irqbit, &irqstatus);
52 } while ((irqstatus == false) && (result == 0U));
53
54 return result;
55 }
56
lpddr4_pollandackirq(const ti_lpddr4_privatedata * pd)57 static uint32_t lpddr4_pollandackirq(const ti_lpddr4_privatedata *pd)
58 {
59 uint32_t result = 0U;
60 lpddr4_ctlregs *ctlregbase = pd->ctlbase;
61
62 result = lpddr4_pollphyindepirq(pd, LPDDR4_INTR_PHY_INDEP_INIT_DONE_BIT,
63 TI_LPDDR4_CUSTOM_TIMEOUT_DELAY);
64 if (result != 0U) {
65 ERROR("lpddr4: init timeout PI=0x%x CTL=0x%x\n",
66 ctlregbase->TI_LPDDR4__PI_INT_STATUS__REG,
67 ctlregbase->DENALI_CTL[342]);
68 return result;
69 }
70
71 result = ti_lpddr4_ackphyindepinterrupt(pd, LPDDR4_INTR_PHY_INDEP_INIT_DONE_BIT);
72 if (result != 0U) {
73 return result;
74 }
75
76 result = lpddr4_pollctlirq(pd, LPDDR4_INTR_MC_INIT_DONE, TI_LPDDR4_CUSTOM_TIMEOUT_DELAY);
77 if (result != 0U) {
78 ERROR("lpddr4: init timeout MC=0x%x CTL=0x%x\n",
79 ctlregbase->TI_LPDDR4__INT_STATUS_MASTER__REG,
80 ctlregbase->DENALI_CTL[342]);
81 return result;
82 }
83
84 result = ti_lpddr4_ackctlinterrupt(pd, LPDDR4_INTR_MC_INIT_DONE);
85
86 return result;
87 }
88
lpddr4_startsequencecontroller(const ti_lpddr4_privatedata * pd)89 static uint32_t lpddr4_startsequencecontroller(const ti_lpddr4_privatedata *pd)
90 {
91 uint32_t result = 0U;
92 uint32_t regval = 0U;
93 ti_lpddr4_infotype infotype;
94 lpddr4_ctlregs *ctlregbase = pd->ctlbase;
95
96 regval = CPS_FLD_SET(TI_LPDDR4__PI_START__FLD,
97 ctlregbase->TI_LPDDR4__PI_START__REG);
98 ctlregbase->TI_LPDDR4__PI_START__REG = regval;
99
100 regval = CPS_FLD_SET(TI_LPDDR4__START__FLD,
101 ctlregbase->TI_LPDDR4__START__REG);
102 ctlregbase->TI_LPDDR4__START__REG = regval;
103
104 if (pd->infohandler != (lpddr4_infocallback)NULL) {
105 infotype = LPDDR4_DRV_SOC_PLL_UPDATE;
106 pd->infohandler(pd, infotype);
107 }
108
109 result = lpddr4_pollandackirq(pd);
110
111 return result;
112 }
113
114
ti_lpddr4_probe(const ti_lpddr4_config * config,uint16_t * configsize)115 uint32_t ti_lpddr4_probe(const ti_lpddr4_config *config, uint16_t *configsize)
116 {
117 if ((configsize == NULL) || (config == NULL)) {
118 return (uint32_t)EINVAL;
119 }
120
121 *configsize = (uint16_t)(sizeof(ti_lpddr4_privatedata));
122 return 0U;
123 }
124
ti_lpddr4_init(ti_lpddr4_privatedata * pd,const ti_lpddr4_config * cfg)125 uint32_t ti_lpddr4_init(ti_lpddr4_privatedata *pd, const ti_lpddr4_config *cfg)
126 {
127 if ((pd == NULL) || (cfg == NULL)) {
128 return (uint32_t)EINVAL;
129 }
130
131 pd->ctlbase = cfg->ctlbase;
132 pd->infohandler = (lpddr4_infocallback)cfg->infohandler;
133 pd->ctlinterrupthandler = (lpddr4_ctlcallback)cfg->ctlinterrupthandler;
134 pd->phyindepinterrupthandler = (lpddr4_phyindepcallback)cfg->phyindepinterrupthandler;
135 return 0U;
136 }
137
ti_lpddr4_start(const ti_lpddr4_privatedata * pd)138 uint32_t ti_lpddr4_start(const ti_lpddr4_privatedata *pd)
139 {
140 uint32_t result = 0U;
141
142 if (pd == NULL) {
143 return (uint32_t)EINVAL;
144 }
145
146 ti_lpddr4_enable_pi_initiator(pd);
147
148 result = lpddr4_startsequencecontroller(pd);
149
150 return result;
151 }
152
ti_lpddr4_readreg(const ti_lpddr4_privatedata * pd,ti_lpddr4_regblock cpp,uint32_t regoffset,uint32_t * regvalue)153 uint32_t ti_lpddr4_readreg(const ti_lpddr4_privatedata *pd, ti_lpddr4_regblock cpp,
154 uint32_t regoffset, uint32_t *regvalue)
155 {
156 if ((pd == NULL) || (regvalue == NULL)) {
157 return (uint32_t)EINVAL;
158 } else if ((cpp != LPDDR4_CTL_REGS) &&
159 (cpp != LPDDR4_PHY_REGS) &&
160 (cpp != LPDDR4_PHY_INDEP_REGS)) {
161 return (uint32_t)EINVAL;
162 }
163
164 lpddr4_ctlregs *ctlregbase = pd->ctlbase;
165
166 switch (cpp) {
167 case LPDDR4_CTL_REGS:
168 if (regoffset >= TI_LPDDR4_INTR_CTL_REG_COUNT) {
169 return (uint32_t)EINVAL;
170 }
171 *regvalue = ctlregbase->DENALI_CTL[regoffset];
172 break;
173 case LPDDR4_PHY_REGS:
174 if (regoffset >= TI_LPDDR4_INTR_PHY_REG_COUNT) {
175 return (uint32_t)EINVAL;
176 }
177 *regvalue = ctlregbase->DENALI_PHY_0[regoffset];
178 break;
179 case LPDDR4_PHY_INDEP_REGS:
180 if (regoffset >= TI_LPDDR4_INTR_PHY_INDEP_REG_COUNT) {
181 return (uint32_t)EINVAL;
182 }
183 *regvalue = ctlregbase->DENALI_PI[regoffset];
184 break;
185 default:
186 break;
187 }
188
189 return 0U;
190 }
191
ti_lpddr4_writereg(const ti_lpddr4_privatedata * pd,ti_lpddr4_regblock cpp,uint32_t regoffset,uint32_t regvalue)192 uint32_t ti_lpddr4_writereg(const ti_lpddr4_privatedata *pd, ti_lpddr4_regblock cpp,
193 uint32_t regoffset, uint32_t regvalue)
194 {
195 if (pd == NULL) {
196 return (uint32_t)EINVAL;
197 } else if ((cpp != LPDDR4_CTL_REGS) &&
198 (cpp != LPDDR4_PHY_REGS) &&
199 (cpp != LPDDR4_PHY_INDEP_REGS)) {
200 return (uint32_t)EINVAL;
201 }
202
203 lpddr4_ctlregs *ctlregbase = pd->ctlbase;
204
205 switch (cpp) {
206 case LPDDR4_CTL_REGS:
207 if (regoffset >= TI_LPDDR4_INTR_CTL_REG_COUNT) {
208 return (uint32_t)EINVAL;
209 }
210 ctlregbase->DENALI_CTL[regoffset] = regvalue;
211 break;
212 case LPDDR4_PHY_REGS:
213 if (regoffset >= TI_LPDDR4_INTR_PHY_REG_COUNT) {
214 return (uint32_t)EINVAL;
215 }
216 ctlregbase->DENALI_PHY_0[regoffset] = regvalue;
217 break;
218 case LPDDR4_PHY_INDEP_REGS:
219 if (regoffset >= TI_LPDDR4_INTR_PHY_INDEP_REG_COUNT) {
220 return (uint32_t)EINVAL;
221 }
222 ctlregbase->DENALI_PI[regoffset] = regvalue;
223 break;
224 default:
225 break;
226 }
227
228 return 0U;
229 }
230
ti_lpddr4_writectlconfigex(const ti_lpddr4_privatedata * pd,const uint32_t regvalues[],uint16_t regcount)231 uint32_t ti_lpddr4_writectlconfigex(const ti_lpddr4_privatedata *pd,
232 const uint32_t regvalues[], uint16_t regcount)
233 {
234 uint32_t result = 0U;
235 uint32_t aindex;
236
237 if ((pd == NULL) || (regvalues == (uint32_t *)NULL)) {
238 return (uint32_t)EINVAL;
239 }
240
241 for (aindex = 0; aindex < regcount; aindex++) {
242 result = ti_lpddr4_writereg(pd, LPDDR4_CTL_REGS, aindex,
243 regvalues[aindex]);
244 if (result != 0U) {
245 return result;
246 }
247 }
248
249 return result;
250 }
251
ti_lpddr4_writephyindepconfigex(const ti_lpddr4_privatedata * pd,const uint32_t regvalues[],uint16_t regcount)252 uint32_t ti_lpddr4_writephyindepconfigex(const ti_lpddr4_privatedata *pd,
253 const uint32_t regvalues[], uint16_t regcount)
254 {
255 uint32_t result = 0U;
256 uint32_t aindex;
257
258 if ((pd == NULL) || (regvalues == (uint32_t *)NULL)) {
259 return (uint32_t)EINVAL;
260 }
261
262 for (aindex = 0; aindex < regcount; aindex++) {
263 result = ti_lpddr4_writereg(pd, LPDDR4_PHY_INDEP_REGS, aindex,
264 regvalues[aindex]);
265 if (result != 0U) {
266 return result;
267 }
268 }
269
270 return result;
271 }
272
ti_lpddr4_writephyconfigex(const ti_lpddr4_privatedata * pd,const uint32_t regvalues[],uint16_t regcount)273 uint32_t ti_lpddr4_writephyconfigex(const ti_lpddr4_privatedata *pd,
274 const uint32_t regvalues[], uint16_t regcount)
275 {
276 uint32_t result = 0U;
277 uint32_t aindex;
278
279 if ((pd == NULL) || (regvalues == (uint32_t *)NULL)) {
280 return (uint32_t)EINVAL;
281 }
282
283 for (aindex = 0; aindex < regcount; aindex++) {
284 result = ti_lpddr4_writereg(pd, LPDDR4_PHY_REGS, aindex,
285 regvalues[aindex]);
286 if (result != 0U) {
287 return result;
288 }
289 }
290
291 return result;
292 }
293
ti_lpddr4_checkphyindepinterrupt(const ti_lpddr4_privatedata * pd,ti_lpddr4_intr_phyindepinterrupt intr,bool * irqstatus)294 uint32_t ti_lpddr4_checkphyindepinterrupt(const ti_lpddr4_privatedata *pd,
295 ti_lpddr4_intr_phyindepinterrupt intr,
296 bool *irqstatus)
297 {
298 uint32_t result = 0;
299 uint32_t phyindepirqstatus = 0;
300
301 result = ti_lpddr4_intr_phyint_sf(pd, intr, irqstatus);
302 if ((result == 0U) && ((uint32_t)intr < TI_WORD_SHIFT)) {
303 lpddr4_ctlregs *ctlregbase = pd->ctlbase;
304
305 phyindepirqstatus = ctlregbase->TI_LPDDR4__PI_INT_STATUS__REG;
306 *irqstatus = (((phyindepirqstatus >> (uint32_t)intr) & TI_LPDDR4_BIT_MASK) > 0U);
307 }
308 return result;
309 }
310
ti_lpddr4_ackphyindepinterrupt(const ti_lpddr4_privatedata * pd,ti_lpddr4_intr_phyindepinterrupt intr)311 uint32_t ti_lpddr4_ackphyindepinterrupt(const ti_lpddr4_privatedata *pd,
312 ti_lpddr4_intr_phyindepinterrupt intr)
313 {
314 uint32_t result = 0U;
315 uint32_t regval = 0U;
316
317 result = ti_lpddr4_intr_ack_phyint_sf(pd, intr);
318 if ((result == 0U) && ((uint32_t)intr < TI_WORD_SHIFT)) {
319 lpddr4_ctlregs *ctlregbase = pd->ctlbase;
320
321 regval = (TI_LPDDR4_BIT_MASK << (uint32_t)intr);
322 ctlregbase->TI_LPDDR4__PI_INT_ACK__REG = regval;
323 }
324
325 return result;
326 }
327