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 "board_ddr.h"
37 #ifdef BUILD_MCU1_0
38 typedef struct Board_DDRThermalMgmtInstance_s
39 {
40 LPDDR4_Config boardDDRCfg;
41 LPDDR4_PrivateData boardRuntimeDDRPd;
42 uint32_t boardDDRInitRefreshRate[LPDDR4_FSP_2+1];
43 HwiP_Handle boardTempInterruptHandle;
44 Board_thermalMgmtCallbackFunction_t appCallBackFunction;
45 } Board_DDRThermalMgmtInstance_t;
47 #ifdef __cplusplus
48 #pragma DATA_SECTION(".data:BOARD_DDR_thermalManagement");
49 #else
50 #pragma DATA_SECTION(gBoard_DDRThermalMgmtInstance, ".data:BOARD_DDR_thermalManagement");
51 #endif
52 static Board_DDRThermalMgmtInstance_t gBoard_DDRThermalMgmtInstance;
54 /* Local defines */
55 #define BOARD_SCICLIENT_RESP_TIMEOUT 1000000
57 #ifdef __cplusplus
58 #pragma DATA_SECTION(".const:BOARD_DDR_thermalManagement");
59 #else
60 #pragma DATA_SECTION(gRefreshRateMultFactor, ".const:BOARD_DDR_thermalManagement");
61 #endif
62 /* Multiplication factors assumes scaling by 8 */
63 static const uint32_t gRefreshRateMultFactor[BOARD_MAX_TEMP_CHECK_REFRESH_RATE_VALUE+1] =
64 {
65 32U, /* 4 x */
66 32U, /* 4 x */
67 16U, /* 2 x */
68 8U, /* 1 x */
69 4U, /* 0.5 x */
70 2U, /* 0.25 x */
71 2U, /* 0.25 x with derating */
72 2U, /* 0.25 x with derating */
73 };
74 #ifdef __cplusplus
75 #pragma DATA_SECTION(".const:BOARD_DDR_thermalManagement");
76 #else
77 #pragma DATA_SECTION(gBoardDDRFSPNum, ".const:BOARD_DDR_thermalManagement");
78 #endif
80 static const LPDDR4_CtlFspNum gBoardDDRFSPNum[LPDDR4_FSP_2+1] =
81 {
82 LPDDR4_FSP_0,
83 LPDDR4_FSP_1,
84 LPDDR4_FSP_2,
85 };
87 #ifdef __cplusplus
88 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
89 #else
90 #pragma CODE_SECTION(Board_updateRefreshRate, ".text:BOARD_DDR_thermalManagement");
91 #endif
93 void Board_updateRefreshRate(const LPDDR4_CtlFspNum fsNum, uint32_t refreshMultFactor)
94 {
95 uint32_t refreshRate;
96 uint32_t status;
98 /* Calculate refresh rate */
99 refreshRate = (gBoard_DDRThermalMgmtInstance.boardDDRInitRefreshRate[fsNum] * refreshMultFactor) >> 3U;
101 /* Take action to update Refresh rate */
102 status = LPDDR4_SetRefreshRate(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), &fsNum, &refreshRate);
104 if (status > 0U)
105 {
106 BOARD_DEBUG_LOG("LPDDR4_GetRefreshRate: FAIL\n");
107 /* Add Assert if needed*/
108 }
109 }
111 #ifdef __cplusplus
112 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
113 #else
114 #pragma CODE_SECTION(Board_updateAllRefreshRate, ".text:BOARD_DDR_thermalManagement");
115 #endif
117 void Board_updateAllRefreshRate(uint32_t refreshMultFactor)
118 {
120 Board_updateRefreshRate(LPDDR4_FSP_0, refreshMultFactor);
121 Board_updateRefreshRate(LPDDR4_FSP_1, refreshMultFactor);
122 Board_updateRefreshRate(LPDDR4_FSP_2, refreshMultFactor);
123 }
125 #ifdef __cplusplus
126 #pragma CODE_SECTION(".text:BOARD_DDR_thermalManagement");
127 #else
128 #pragma CODE_SECTION(Board_DDRInterruptHandler, ".text:BOARD_DDR_thermalManagement");
129 #endif
130 /**
131 * \brief Interrupt handler for DDR events
132 *
133 * Handles DDR events including temperature change events.
134 *
135 * \return BOARD_SOK in case of success or appropriate error code
136 *
137 */
138 void Board_DDRInterruptHandler(uintptr_t arg)
139 {
140 bool irqStatus;
141 uint32_t regValue;
142 uint32_t status;
143 uint32_t tempCheckRefreshRateIndex;
145 /* Get DDR interrupt status to check on thermal events */
146 LPDDR4_CheckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_CHANGE, &irqStatus);
147 if (irqStatus)
148 {
149 /* Check Temp check register to check on the temperature change:
150 * Decide on refresh rate
151 */
152 /* Read Temp check register */
153 status = LPDDR4_ReadReg(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_CTL_REGS,
154 LPDDR4__AUTO_TEMPCHK_VAL_0__REG_OFFSET,
155 ®Value);
156 if (status == 0U)
157 {
158 /* Calculate refresh rate index */
159 tempCheckRefreshRateIndex = ((regValue & LPDDR4__AUTO_TEMPCHK_VAL_0_MASK)
160 >> LPDDR4__AUTO_TEMPCHK_VAL_0_SHIFT )
161 & LPDDR4__AUTO_TEMPCHK_OP0_MASK;
163 /* Adjust refresh rate */
164 Board_updateAllRefreshRate(gRefreshRateMultFactor[tempCheckRefreshRateIndex]);
166 /* Call application callback function */
167 if (gBoard_DDRThermalMgmtInstance.appCallBackFunction != NULL)
168 {
169 gBoard_DDRThermalMgmtInstance.appCallBackFunction((Board_DDRTempEventType)
170 (BOARD_DDR_TEMP_EVENT_LOW_TEMP_ALARM
171 + tempCheckRefreshRateIndex));
172 }
173 }
175 /* Ack interrupt */
176 LPDDR4_AckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_CHANGE);
177 }
178 /* Get DDR interrupt status to check on alarm events */
179 LPDDR4_CheckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_ALERT, &irqStatus);
180 if (irqStatus)
181 {
182 /* High or Low temperature alarm : call application callback */
183 if (gBoard_DDRThermalMgmtInstance.appCallBackFunction != NULL)
184 {
185 gBoard_DDRThermalMgmtInstance.appCallBackFunction(BOARD_DDR_TEMP_EVENT_TEMP_ALERT);
186 }
187 /* Ack interrupt */
188 LPDDR4_AckCtlInterrupt (&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), LPDDR4_TEMP_ALERT);
189 }
191 /* NOTE: Code to handle other DDR events can be added here */
193 }
195 /**
196 * \brief DDR Temperature monitoring initialization function
197 *
198 * Configures and initializes DDR temperature handling.
199 * NOTE: This assumes that the DDR parameters are already have the thermal events
200 * enabled in the DDR controller. This only handles software handling of the
201 * thermal events
202 *
203 * \return BOARD_SOK in case of success or appropriate error code
204 *
205 */
206 Board_STATUS Board_DDRTempMonitoringInit(Board_thermalMgmtCallbackFunction_t callbackFunction)
207 {
208 Board_STATUS status = BOARD_SOK;
209 OsalRegisterIntrParams_t intrPrms;
210 OsalInterruptRetCode_e osalRet;
211 uint64_t interruptMask;
212 uint32_t lpddrStatus;
213 uint32_t fspIndex;
215 struct tisci_msg_rm_irq_release_req irq_release_req =
216 {
217 .ia_id = 0,
218 .vint = 0,
219 .global_event = 0,
220 .vint_status_bit_index = 0,
221 };
222 struct tisci_msg_rm_irq_set_req irq_set_req =
223 {
224 .ia_id = 0,
225 .vint = 0,
226 .global_event = 0,
227 .vint_status_bit_index = 0,
228 };
229 struct tisci_msg_rm_irq_set_resp resp;
231 /* Initialize LPDDR4 driver */
232 gBoard_DDRThermalMgmtInstance.boardDDRCfg.ctlBase = (struct LPDDR4_CtlRegs_s *)BOARD_DDR_SS_BASE;
234 status = LPDDR4_Init(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), &(gBoard_DDRThermalMgmtInstance.boardDDRCfg));
236 if ((status > 0U) ||
237 (gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd.ctlBase != (struct LPDDR4_CtlRegs_s *)gBoard_DDRThermalMgmtInstance.boardDDRCfg.ctlBase))
238 {
239 BOARD_DEBUG_LOG("LPDDR4_Init: FAIL\n");
240 status = BOARD_FAIL;
241 }
243 if (status == BOARD_SOK)
244 {
245 /* Read and preserve the initial Refresh Rates as baseline */
246 for (fspIndex = 0; fspIndex <= LPDDR4_FSP_2; fspIndex++)
247 {
248 lpddrStatus = LPDDR4_GetRefreshRate(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), &gBoardDDRFSPNum[fspIndex],
249 &(gBoard_DDRThermalMgmtInstance.boardDDRInitRefreshRate[gBoardDDRFSPNum[fspIndex]]));
250 if (lpddrStatus > 0U)
251 {
252 BOARD_DEBUG_LOG("LPDDR4_GetRefreshRate: FAIL\n");
253 status = BOARD_FAIL;
254 break;
255 }
256 }
257 }
259 if (status == BOARD_SOK)
260 {
261 /* Configure interrupt router to route DDR Ctrl interrupt to Monitoring
262 * CPU
263 */
264 /* Request irq release for specified interrupt source */
265 irq_release_req.valid_params = TISCI_MSG_VALUE_RM_DST_ID_VALID
266 | TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
267 irq_release_req.src_id = TISCI_DEV_DDR0;
268 irq_release_req.src_index = 0; /* First interrupt in DDR group is the DDR controller interrupt */
269 irq_release_req.dst_id = TISCI_DEV_MCU_R5FSS0_CORE0;
270 irq_release_req.dst_host_irq = CSLR_MCU_R5FSS0_CORE0_INTR_MAIN2MCU_LVL_INTRTR0_OUTL_63;
272 /* Call irq Release */
273 if (CSL_PASS != Sciclient_rmIrqRelease(&irq_release_req, BOARD_SCICLIENT_RESP_TIMEOUT))
274 {
275 /* Ignore if the release fails, as this is expected the first time the image is booted */
276 }
278 /* Request irq set for specified interrupt source */
279 irq_set_req.valid_params = TISCI_MSG_VALUE_RM_DST_ID_VALID
280 | TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
281 irq_set_req.src_id = TISCI_DEV_DDR0;
282 irq_set_req.src_index = 0; /* First interrupt in DDR group is the DDR controller interrupt */
283 irq_set_req.dst_id = TISCI_DEV_MCU_R5FSS0_CORE0;
284 irq_set_req.dst_host_irq = CSLR_MCU_R5FSS0_CORE0_INTR_MAIN2MCU_LVL_INTRTR0_OUTL_63;
286 /* Call irq Set */
287 if (CSL_PASS != Sciclient_rmIrqSet(&irq_set_req, &resp, BOARD_SCICLIENT_RESP_TIMEOUT))
288 {
289 status = BOARD_FAIL;
290 }
291 }
293 if (status == BOARD_SOK)
294 {
296 /* Register DDR Control event handler */
297 Osal_RegisterInterrupt_initParams(&intrPrms);
299 intrPrms.corepacConfig.corepacEventNum = 0;
300 intrPrms.corepacConfig.intVecNum = CSLR_MCU_R5FSS0_CORE0_INTR_MAIN2MCU_LVL_INTRTR0_OUTL_63;
301 intrPrms.corepacConfig.arg = (uintptr_t)(&(gBoard_DDRThermalMgmtInstance.boardTempInterruptHandle));
302 intrPrms.corepacConfig.isrRoutine = &Board_DDRInterruptHandler;
304 /* Clear Interrupt */
305 Osal_ClearInterrupt(intrPrms.corepacConfig.corepacEventNum, intrPrms.corepacConfig.intVecNum);
307 /* Register interrupts */
308 osalRet = Osal_RegisterInterrupt(&intrPrms, &(gBoard_DDRThermalMgmtInstance.boardTempInterruptHandle));
309 if (osalRet != OSAL_INT_SUCCESS)
310 {
311 status = BOARD_FAIL;
312 }
313 }
315 if (status == BOARD_SOK)
316 {
317 /* Unmask only DDR thermal interrupt events */
318 interruptMask = (uint64_t)(0xffffffffffffffffU)
319 & (~(((((uint64_t)1U) << LPDDR4_TEMP_CHANGE)
320 | (((uint64_t)1U) << LPDDR4_TEMP_ALERT))));
321 LPDDR4_SetCtlInterruptMask(&(gBoard_DDRThermalMgmtInstance.boardRuntimeDDRPd), (const uint64_t *)(&interruptMask));
323 /* Enable DDR controller interrupt */
324 Osal_EnableInterrupt(intrPrms.corepacConfig.corepacEventNum, intrPrms.corepacConfig.intVecNum);
325 }
327 if (status == BOARD_SOK)
328 {
329 gBoard_DDRThermalMgmtInstance.appCallBackFunction = NULL;
330 if (callbackFunction != NULL)
331 {
332 /* Register callback function */
333 gBoard_DDRThermalMgmtInstance.appCallBackFunction = callbackFunction;
334 }
335 }
336 return status;
337 }
338 #endif /* BUILD_MCU1_0 */