1 /******************************************************************************
2 * Copyright (c) 2019 Texas Instruments Incorporated - http://www.ti.com
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the
14 * distribution.
15 *
16 * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *****************************************************************************/
33 #include <string.h>
34 #include <ti/csl/csl_lpddr.h>
35 #include <ti/csl/arch/csl_arch.h>
36 #include "board_ddr.h"
38 #ifdef BUILD_MCU
39 typedef struct Board_DDRThermalMgmtInstance_s
40 {
41 LPDDR4_Config boardDDRCfg;
42 LPDDR4_PrivateData boardRuntimeDDRPd;
43 uint32_t boardDDRInitRefreshRate[LPDDR4_FSP_2+1];
44 uint32_t boardDDRTrasMax[LPDDR4_FSP_2+1];
45 HwiP_Handle boardTempInterruptHandle;
46 Board_thermalMgmtCallbackFunction_t appCallBackFunction;
47 uint16_t devIdCore;
48 uint16_t devIdIr;
49 } Board_DDRThermalMgmtInstance_t;
51 #ifdef __cplusplus
52 #pragma DATA_SECTION(".data:BOARD_DDR_thermalManagement");
53 #else
54 #pragma DATA_SECTION(gBoard_DDRThermalMgmtInstance, ".data:BOARD_DDR_thermalManagement");
55 #endif
56 static Board_DDRThermalMgmtInstance_t gBoard_DDRThermalMgmtInstance;
58 /* Local defines */
59 #define BOARD_SCICLIENT_RESP_TIMEOUT 1000000
61 #ifdef __cplusplus
62 #pragma DATA_SECTION(".const:BOARD_DDR_thermalManagement");
63 #else
64 #pragma DATA_SECTION(gRefreshRateMultFactor, ".const:BOARD_DDR_thermalManagement");
65 #endif
66 /* Multiplication factors assumes scaling by 8 */
67 static const uint32_t gRefreshRateMultFactor[BOARD_MAX_TEMP_CHECK_REFRESH_RATE_VALUE+1] =
68 {
69 32U, /* 4 x */
70 32U, /* 4 x */
71 16U, /* 2 x */
72 8U, /* 1 x */
73 4U, /* 0.5 x */
74 2U, /* 0.25 x */
75 2U, /* 0.25 x with derating */
76 2U, /* 0.25 x with derating */
77 };
78 #ifdef __cplusplus
79 #pragma DATA_SECTION(".const:BOARD_DDR_thermalManagement");
80 #else
81 #pragma DATA_SECTION(gBoardDDRFSPNum, ".const:BOARD_DDR_thermalManagement");
82 #endif
84 static const LPDDR4_CtlFspNum gBoardDDRFSPNum[LPDDR4_FSP_2+1] =
85 {
86 LPDDR4_FSP_0,
87 LPDDR4_FSP_1,
88 LPDDR4_FSP_2,
89 };
91 #ifdef __cplusplus
92 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
93 #else
94 #pragma CODE_SECTION(Board_updateRefreshRate, ".text:BOARD_DDR_thermalManagement");
95 #endif
97 void Board_updateRefreshRate(const LPDDR4_CtlFspNum fsNum, uint32_t refreshMultFactor)
98 {
99 uint32_t refreshRate;
100 uint32_t trasMax;
101 uint32_t status;
103 /* Calculate refresh rate */
104 refreshRate = (gBoard_DDRThermalMgmtInstance.boardDDRInitRefreshRate[fsNum] * refreshMultFactor) >> 3U;
105 trasMax = (gBoard_DDRThermalMgmtInstance.boardDDRTrasMax[fsNum] * refreshMultFactor) >> 3U;
107 /* Take action to update Refresh rate */
108 status = LPDDR4_SetRefreshRate(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), &fsNum, &refreshRate, &trasMax);
110 if (status > 0U)
111 {
112 BOARD_DEBUG_LOG("LPDDR4_GetRefreshRate: FAIL\n");
113 /* Add Assert if needed*/
114 }
115 }
117 #ifdef __cplusplus
118 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
119 #else
120 #pragma CODE_SECTION(Board_DDRSetDevId, ".text:BOARD_DDR_thermalManagement");
121 #endif
123 static void Board_DDRSetDevId()
124 {
125 CSL_ArmR5CPUInfo info;
127 CSL_armR5GetCpuID(&info);
128 if (info.grpId == (uint32_t)CSL_ARM_R5_CLUSTER_GROUP_ID_0)
129 {
130 /* MCU SS Pulsar R5 SS */
131 /* For R5 cores in the MCU domain MAIN2MCU_LVL_INTRTR0 is the base interrupt to the VIM. */
132 gBoard_DDRThermalMgmtInstance.devIdIr = TISCI_DEV_MAIN2MCU_LVL_INTRTR0;
133 gBoard_DDRThermalMgmtInstance.devIdCore = (info.cpuID == CSL_ARM_R5_CPU_ID_0)?
134 TISCI_DEV_MCU_R5FSS0_CORE0:
135 TISCI_DEV_MCU_R5FSS0_CORE1;
136 }
137 else if (info.grpId == (uint32_t)CSL_ARM_R5_CLUSTER_GROUP_ID_1)
138 {
139 /* MAIN SS Pulsar R5 SS0 */
140 gBoard_DDRThermalMgmtInstance.devIdIr = TISCI_DEV_R5FSS0_INTROUTER0;
141 gBoard_DDRThermalMgmtInstance.devIdCore = (info.cpuID == CSL_ARM_R5_CPU_ID_0)?
142 TISCI_DEV_R5FSS0_CORE0:
143 TISCI_DEV_R5FSS0_CORE1;
144 }
145 else if (info.grpId == (uint32_t)CSL_ARM_R5_CLUSTER_GROUP_ID_2)
146 {
147 /* MAIN SS Pulsar R5 SS1 */
148 gBoard_DDRThermalMgmtInstance.devIdIr = TISCI_DEV_R5FSS1_INTROUTER0;
149 gBoard_DDRThermalMgmtInstance.devIdCore = (info.cpuID == CSL_ARM_R5_CPU_ID_0)?
150 TISCI_DEV_R5FSS1_CORE0:
151 TISCI_DEV_R5FSS1_CORE1;
152 }
154 return;
155 }
157 #ifdef __cplusplus
158 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
159 #else
160 #pragma CODE_SECTION(Board_DDRGetIntNum, ".text:BOARD_DDR_thermalManagement");
161 #endif
163 static Board_STATUS Board_DDRGetIntNum(uint16_t *coreInterruptIdx)
164 {
165 Board_STATUS status = BOARD_SOK;
166 uint16_t irIntrIdx;
167 struct tisci_msg_rm_get_resource_range_resp res = {0};
168 struct tisci_msg_rm_get_resource_range_req req = {0};
170 req.type = gBoard_DDRThermalMgmtInstance.devIdIr;
171 req.subtype = (uint8_t)TISCI_RESASG_SUBTYPE_IR_OUTPUT;
172 req.secondary_host = (uint8_t)TISCI_MSG_VALUE_RM_UNUSED_SECONDARY_HOST;
174 res.range_num = 0;
175 res.range_start = 0;
177 /* Get interrupt number range */
178 status = Sciclient_rmGetResourceRange(
179 &req,
180 &res,
181 SCICLIENT_SERVICE_WAIT_FOREVER);
182 if (CSL_PASS != status || res.range_num == 0) {
183 /* Try with HOST_ID_ALL */
184 req.type = gBoard_DDRThermalMgmtInstance.devIdIr;
185 req.subtype = (uint8_t)TISCI_RESASG_SUBTYPE_IR_OUTPUT;
186 req.secondary_host = TISCI_HOST_ID_ALL;
188 status = Sciclient_rmGetResourceRange(
189 &req,
190 &res,
191 SCICLIENT_SERVICE_WAIT_FOREVER);
192 }
193 if ((CSL_PASS == status) && (res.range_num != 0))
194 {
195 /* Translate IR Idx to Core Interrupt Idx */
196 irIntrIdx = res.range_start;
197 status = Sciclient_rmIrqTranslateIrOutput(gBoard_DDRThermalMgmtInstance.devIdIr,
198 irIntrIdx,
199 gBoard_DDRThermalMgmtInstance.devIdCore,
200 coreInterruptIdx);
201 }
202 else
203 {
204 status = BOARD_FAIL;
205 }
207 return status;
208 }
210 #ifdef __cplusplus
211 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
212 #else
213 #pragma CODE_SECTION(Board_updateAllRefreshRate, ".text:BOARD_DDR_thermalManagement");
214 #endif
216 void Board_updateAllRefreshRate(uint32_t refreshMultFactor)
217 {
219 Board_updateRefreshRate(LPDDR4_FSP_0, refreshMultFactor);
220 Board_updateRefreshRate(LPDDR4_FSP_1, refreshMultFactor);
221 Board_updateRefreshRate(LPDDR4_FSP_2, refreshMultFactor);
222 }
224 #ifdef __cplusplus
225 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
226 #else
227 #pragma CODE_SECTION(Board_DDRInterruptHandler, ".text:BOARD_DDR_thermalManagement");
228 #endif
229 /**
230 * \brief Interrupt handler for DDR events
231 *
232 * Handles DDR events including temperature change events.
233 *
234 * \return BOARD_SOK in case of success or appropriate error code
235 *
236 */
237 void Board_DDRInterruptHandler(uintptr_t arg)
238 {
239 bool irqStatus;
240 uint32_t regValue;
241 uint32_t status;
242 uint32_t tempCheckRefreshRateIndex;
244 /* Get DDR interrupt status to check on thermal events */
245 LPDDR4_CheckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_CHANGE, &irqStatus);
246 if (irqStatus)
247 {
248 /* Check Temp check register to check on the temperature change:
249 * Decide on refresh rate
250 */
251 /* Read Temp check register */
252 status = LPDDR4_ReadReg(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_CTL_REGS,
253 LPDDR4__AUTO_TEMPCHK_VAL_0__REG_OFFSET,
254 ®Value);
255 if (status == 0U)
256 {
257 /* Calculate refresh rate index */
258 tempCheckRefreshRateIndex = ((regValue & LPDDR4__AUTO_TEMPCHK_VAL_0_MASK)
259 >> LPDDR4__AUTO_TEMPCHK_VAL_0_SHIFT )
260 & LPDDR4__AUTO_TEMPCHK_OP0_MASK;
262 /* Adjust refresh rate */
263 Board_updateAllRefreshRate(gRefreshRateMultFactor[tempCheckRefreshRateIndex]);
265 /* Call application callback function */
266 if (gBoard_DDRThermalMgmtInstance.appCallBackFunction != NULL)
267 {
268 gBoard_DDRThermalMgmtInstance.appCallBackFunction((Board_DDRTempEventType)
269 (BOARD_DDR_TEMP_EVENT_LOW_TEMP_ALARM
270 + tempCheckRefreshRateIndex));
271 }
272 }
274 /* Ack interrupt */
275 LPDDR4_AckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_CHANGE);
276 }
277 /* Get DDR interrupt status to check on alarm events */
278 LPDDR4_CheckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_ALERT, &irqStatus);
279 if (irqStatus)
280 {
281 /* High or Low temperature alarm : call application callback */
282 if (gBoard_DDRThermalMgmtInstance.appCallBackFunction != NULL)
283 {
284 gBoard_DDRThermalMgmtInstance.appCallBackFunction(BOARD_DDR_TEMP_EVENT_TEMP_ALERT);
285 }
286 /* Ack interrupt */
287 LPDDR4_AckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_ALERT);
288 }
290 /* NOTE: Code to handle other DDR events can be added here */
292 }
294 /**
295 * \brief DDR Temperature monitoring initialization function
296 *
297 * Configures and initializes DDR temperature handling.
298 * NOTE: This assumes that the DDR parameters are already have the thermal events
299 * enabled in the DDR controller. This only handles software handling of the
300 * thermal events
301 *
302 * \return BOARD_SOK in case of success or appropriate error code
303 *
304 */
305 Board_STATUS Board_DDRTempMonitoringInit(Board_thermalMgmtCallbackFunction_t callbackFunction)
306 {
307 Board_STATUS status = BOARD_SOK;
308 OsalRegisterIntrParams_t intrPrms;
309 OsalInterruptRetCode_e osalRet;
310 uint64_t interruptMask;
311 uint32_t lpddrStatus;
312 uint32_t fspIndex;
313 uint16_t coreInterruptIdx;
315 struct tisci_msg_rm_irq_release_req irq_release_req =
316 {
317 .ia_id = 0,
318 .vint = 0,
319 .global_event = 0,
320 .vint_status_bit_index = 0,
321 };
322 struct tisci_msg_rm_irq_set_req irq_set_req =
323 {
324 .ia_id = 0,
325 .vint = 0,
326 .global_event = 0,
327 .vint_status_bit_index = 0,
328 };
329 struct tisci_msg_rm_irq_set_resp resp;
331 /* Initialize LPDDR4 driver */
332 gBoard_DDRThermalMgmtInstance.boardDDRCfg.ctlBase = (struct LPDDR4_CtlRegs_s *)BOARD_DDR_CTL_CFG_BASE;
334 status = LPDDR4_Init(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), &(gBoard_DDRThermalMgmtInstance.boardDDRCfg));
336 if ((status > 0U) ||
337 (gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd.ctlBase != (struct LPDDR4_CtlRegs_s *)gBoard_DDRThermalMgmtInstance.boardDDRCfg.ctlBase))
338 {
339 BOARD_DEBUG_LOG("LPDDR4_Init: FAIL\n");
340 status = BOARD_FAIL;
341 }
343 if (status == BOARD_SOK)
344 {
345 /* Read and preserve the initial Refresh Rates as baseline */
346 for (fspIndex = 0; fspIndex <= LPDDR4_FSP_2; fspIndex++)
347 {
348 lpddrStatus = LPDDR4_GetRefreshRate(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), &gBoardDDRFSPNum[fspIndex],
349 &(gBoard_DDRThermalMgmtInstance.boardDDRInitRefreshRate[gBoardDDRFSPNum[fspIndex]]),
350 &(gBoard_DDRThermalMgmtInstance.boardDDRTrasMax[gBoardDDRFSPNum[fspIndex]]));
351 if (lpddrStatus > 0U)
352 {
353 BOARD_DEBUG_LOG("LPDDR4_GetRefreshRate: FAIL\n");
354 status = BOARD_FAIL;
355 break;
356 }
357 }
358 }
360 if (status == BOARD_SOK)
361 {
362 Board_DDRSetDevId();
363 /* Get the Core IRQ Idx */
364 status = Board_DDRGetIntNum(&coreInterruptIdx);
365 }
367 if (status == BOARD_SOK)
368 {
369 /* Configure interrupt router to route DDR Ctrl interrupt to Monitoring
370 * CPU
371 */
372 /* Request irq release for specified interrupt source */
373 irq_release_req.valid_params = TISCI_MSG_VALUE_RM_DST_ID_VALID
374 | TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
375 irq_release_req.src_id = TISCI_DEV_DDR0;
376 irq_release_req.src_index = 0; /* First interrupt in DDR group is the DDR controller interrupt */
377 irq_release_req.dst_id = gBoard_DDRThermalMgmtInstance.devIdCore;
378 irq_release_req.dst_host_irq = coreInterruptIdx;
380 /* Call irq Release */
381 if (CSL_PASS != Sciclient_rmIrqRelease(&irq_release_req, BOARD_SCICLIENT_RESP_TIMEOUT))
382 {
383 /* Ignore if the release fails, as this is expected the first time the image is booted */
384 }
386 /* Request irq set for specified interrupt source */
387 irq_set_req.valid_params = TISCI_MSG_VALUE_RM_DST_ID_VALID
388 | TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
389 irq_set_req.src_id = TISCI_DEV_DDR0;
390 irq_set_req.src_index = 0; /* First interrupt in DDR group is the DDR controller interrupt */
391 irq_set_req.dst_id = gBoard_DDRThermalMgmtInstance.devIdCore;
392 irq_set_req.dst_host_irq = coreInterruptIdx;
394 /* Call irq Set */
395 if (CSL_PASS != Sciclient_rmIrqSet(&irq_set_req, &resp, BOARD_SCICLIENT_RESP_TIMEOUT))
396 {
397 status = BOARD_FAIL;
398 }
399 }
401 if (status == BOARD_SOK)
402 {
404 /* Register DDR Control event handler */
405 Osal_RegisterInterrupt_initParams(&intrPrms);
407 intrPrms.corepacConfig.corepacEventNum = 0;
408 intrPrms.corepacConfig.intVecNum = coreInterruptIdx;
409 intrPrms.corepacConfig.arg = (uintptr_t)(&(gBoard_DDRThermalMgmtInstance.boardTempInterruptHandle));
410 intrPrms.corepacConfig.isrRoutine = &Board_DDRInterruptHandler;
412 /* Clear Interrupt */
413 Osal_ClearInterrupt(intrPrms.corepacConfig.corepacEventNum, intrPrms.corepacConfig.intVecNum);
415 /* Register interrupts */
416 osalRet = Osal_RegisterInterrupt(&intrPrms, &(gBoard_DDRThermalMgmtInstance.boardTempInterruptHandle));
417 if (osalRet != OSAL_INT_SUCCESS)
418 {
419 status = BOARD_FAIL;
420 }
421 }
423 if (status == BOARD_SOK)
424 {
425 /* Unmask only DDR thermal interrupt events */
426 interruptMask = (uint64_t)(0xffffffffffffffffU)
427 & (~(((((uint64_t)1U) << LPDDR4_TEMP_CHANGE)
428 | (((uint64_t)1U) << LPDDR4_TEMP_ALERT))));
429 LPDDR4_SetCtlInterruptMask(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), (const uint64_t *)(&interruptMask));
431 /* Enable DDR controller interrupt */
432 Osal_EnableInterrupt(intrPrms.corepacConfig.corepacEventNum, intrPrms.corepacConfig.intVecNum);
433 }
435 if (status == BOARD_SOK)
436 {
437 gBoard_DDRThermalMgmtInstance.appCallBackFunction = NULL;
438 if (callbackFunction != NULL)
439 {
440 /* Register callback function */
441 gBoard_DDRThermalMgmtInstance.appCallBackFunction = callbackFunction;
442 }
443 }
444 return status;
445 }
447 #endif /* BUILD_MCU*/