Skip to content

Commit 6c343d6

Browse files
ti-scariaDhruvaG2000
authored andcommitted
PENDING: feat(ti): Introduce low-latency CPU standby mode
Introduce a new low-latency CPU standby mode using the OSI mode in PSCI and leveraging the cpuidle framework in the kernel. Using the cpuidle framework, when the cores are idle, the cpuidle governor opportunistically selects among the idle-states that are present to enter a low power state. In the low latency cluster idle state implemented: DDR is in auto self refresh, auto clock gating is enabled, few LPSCs are disabled and PLLs are in Bypass or running at low frequency to save power. Signed-off-by: Scaria Kochidanadu <[email protected]>
1 parent 49857bf commit 6c343d6

File tree

4 files changed

+401
-185
lines changed

4 files changed

+401
-185
lines changed

plat/ti/k3/board/am62l/lpm/lpm.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ BL31_SOURCES += \
1111
${PLAT_PATH}/board/am62l/lpm/lpm_trace.c \
1212
${PLAT_PATH}/board/am62l/lpm/pll_16fft_raw.c \
1313
${PLAT_PATH}/board/am62l/lpm/psc_raw.c \
14+
${PLAT_PATH}/board/am62l/lpm/standby.c \
1415
${PLAT_PATH}/board/am62l/lpm/rtc.c
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* Copyright (c) 2026, ARM Limited and Contributors. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include <assert.h>
8+
#include <lib/mmio.h>
9+
#include <board_def.h>
10+
#include <lpm_stub.h>
11+
#include <standby.h>
12+
13+
#define MAIN_PSC_BASE 0x00400000
14+
#define MAIN_PSC_MDCTL_BASE 0x00400A00
15+
#define MAIN_PSC_MDSTAT_BASE 0x00400800
16+
#define MAIN_PSC_PDCTL_BASE 0x00400300
17+
#define MAIN_PSC_PDSTAT_BASE 0x00400200
18+
#define MAIN_PSC_PTSTAT (MAIN_PSC_BASE + PSC_PTSTAT)
19+
#define MAIN_PSC_PTCMD (MAIN_PSC_BASE + PSC_PTCMD)
20+
21+
#define PSC_PTCMD 0x120
22+
#define PSC_PTSTAT 0x128
23+
24+
#define PSC_TIMEOUT_US 100000 /* 100ms timeout */
25+
26+
#define MAIN_PLL_MMR_CFG_BASE (0x04060000UL)
27+
#define WKUP_PLL_MMR_CFG_BASE (0x04040000UL)
28+
29+
#define MAIN_PLL0_HSDIVx(x) MAIN_PLL_MMR_CFG_BASE + 0x80 + (0x4 * x)
30+
#define MAIN_PLL8_BASE MAIN_PLL_MMR_CFG_BASE + 0x1000 * 8
31+
#define MAIN_PLL8_CTRL MAIN_PLL8_BASE + 0x20
32+
#define MAIN_PLL17_BASE MAIN_PLL_MMR_CFG_BASE + 0x1000 * 17
33+
#define MAIN_PLL17_CTRL MAIN_PLL17_BASE + 0x20
34+
#define WKUP_MAIN_PLL0_HSDIVx(x) WKUP_PLL_MMR_CFG_BASE + 0x80 + (0x4 * x)
35+
36+
#define LPSC_ADDR(lpsc_id) MAIN_PSC_MDSTAT_BASE + (4 * lpsc_id)
37+
#define PSC_ADDR(psc_id) MAIN_PSC_PDSTAT_BASE + (4 * psc_id)
38+
39+
#define EMIF_CTLCFG_DENALI_CTL_168 0x0F3082A0
40+
#define EMIF_CTLCFG_DENALI_CTL_169 0x0F3082A4
41+
#define EMIF_CTLCFG_DENALI_CTL_167 0x0F30829C
42+
43+
#define WKUP_CTRL_MMR_CFG5_CLKGATE_CTRL0 0x43054050
44+
45+
#define LPSC_COUNT 4
46+
47+
static unsigned int lpsc_id[] = {
48+
1, /* LPSC_main_gp_test */
49+
2, /* LPSC_main_gp_pbist0 */
50+
33, /* LPSC_mainip_pbist */
51+
39, /* LPSC_main_mpu_clst0_pbist */
52+
};
53+
54+
static unsigned int psc_id[] = {0,0,3,4};
55+
56+
struct am62l_pm_state {
57+
uint32_t pll_hsdiv_val[11];
58+
uint32_t lpsc_value[LPSC_COUNT];
59+
uint32_t ddr_reg[3];
60+
uint32_t auto_clk_gate;
61+
};
62+
63+
static volatile struct am62l_pm_state saved_state;
64+
65+
/*
66+
* Sets the requested state of required module and power domain.
67+
* This function:
68+
* 1. Checks if the requested states are already set
69+
* 2. Waits for any ongoing power state transitions to complete
70+
* 3. Programs the PDCTL and MDCTL registers with the new states
71+
* 4. Initiates the power state transition
72+
* 5. Waits for the transition to complete if powering on
73+
* 6. Logs the before and after states for debugging
74+
*
75+
* @pd_id: Power domain ID (e.g., PD_MPU_CLST, PD_MPU_CLST_CORE_0)
76+
* @md_id: Module ID (e.g., LPSC_MAIN_MPU_CLST, LPSC_MAIN_MPU_CLST_CORE_0)
77+
* @pd_state: Target power domain state (PSC_PD_ON or PSC_PD_OFF)
78+
* @md_state: Target module state (PSC_ENABLE, PSC_DISABLE, PSC_SYNCRESETDISABLE, etc.)
79+
*/
80+
void set_main_psc_state(uint32_t pd_id, uint32_t md_id, uint32_t pd_state, uint32_t md_state)
81+
{
82+
uintptr_t mdctrl_ptr, mdstat_ptr, pdctrl_ptr, pdstat_ptr;
83+
volatile uint32_t mdctrl, mdstat, pdctrl, pdstat, psc_ptstat, psc_ptcmd;
84+
uint64_t tick_start, timeout_ticks;
85+
uint32_t ticks_per_us;
86+
87+
// Calculate addresses with simplified approach
88+
mdctrl_ptr = MAIN_PSC_MDCTL_BASE + (4 * md_id);
89+
mdstat_ptr = MAIN_PSC_MDSTAT_BASE + (4 * md_id);
90+
pdctrl_ptr = MAIN_PSC_PDCTL_BASE + (4 * pd_id);
91+
pdstat_ptr = MAIN_PSC_PDSTAT_BASE + (4 * pd_id);
92+
93+
// Use mmio_read_32 with simplified addresses
94+
mdctrl = mmio_read_32(mdctrl_ptr);
95+
mdstat = mmio_read_32(mdstat_ptr);
96+
pdctrl = mmio_read_32(pdctrl_ptr);
97+
pdstat = mmio_read_32(pdstat_ptr);
98+
99+
INFO("%s: before: md_id=%d, mdstat=0x%x, pdstat=0x%x\n", __func__, md_id, mdstat, pdstat);
100+
if (((pdstat & 0x1) == pd_state) && ((mdstat & 0x1f) == md_state))
101+
return;
102+
103+
// Calculate timeout parameters
104+
ticks_per_us = plat_get_syscnt_freq2() / 1000000;
105+
tick_start = (uint32_t)read_cntpct_el0();
106+
timeout_ticks = PSC_TIMEOUT_US * ticks_per_us;
107+
108+
// wait for GOSTAT to clear
109+
psc_ptstat = mmio_read_32(MAIN_PSC_PTSTAT);
110+
111+
while ((psc_ptstat & (0x1 << pd_id)) != 0) {
112+
if (((uint32_t)read_cntpct_el0() - tick_start) > timeout_ticks) {
113+
ERROR("PSC timeout waiting for initial GOSTAT to clear for md_id %d and pd_id %d\n",
114+
md_id ,pd_id);
115+
break;
116+
}
117+
psc_ptstat = mmio_read_32(MAIN_PSC_PTSTAT);
118+
}
119+
120+
// Set PDCTL NEXT to new state
121+
mmio_write_32(pdctrl_ptr, (pdctrl & ~(0x1)) | pd_state);
122+
// Set MDCTL NEXT to new state
123+
mmio_write_32(mdctrl_ptr, (mdctrl & ~(0x1f)) | md_state);
124+
// Start power transition by setting PTCMD Go to 1
125+
psc_ptcmd = mmio_read_32(MAIN_PSC_PTCMD);
126+
psc_ptcmd |= (0x1 << pd_id);
127+
mmio_write_32(MAIN_PSC_PTCMD, psc_ptcmd);
128+
// return early in case powering off
129+
// This prevents the core from timing out waiting for GOSTAT to clear
130+
if (md_state == PSC_SYNCRESETDISABLE)
131+
return;
132+
133+
// Reset timeout for second wait
134+
tick_start = (uint32_t)read_cntpct_el0();
135+
136+
// Initial read
137+
psc_ptstat = mmio_read_32(MAIN_PSC_PTSTAT);
138+
139+
// Wait loop with timeout
140+
while ((psc_ptstat & (0x1 << pd_id)) != 0) {
141+
if (((uint32_t)read_cntpct_el0() - tick_start) > timeout_ticks) {
142+
ERROR("PSC timeout waiting for GOSTAT to clear for md_id %d and pd_id %d\n",md_id ,pd_id);
143+
break;
144+
}
145+
psc_ptstat = mmio_read_32(MAIN_PSC_PTSTAT);
146+
}
147+
148+
//check states
149+
mdstat = mmio_read_32(mdstat_ptr);
150+
pdstat = mmio_read_32(pdstat_ptr);
151+
INFO("%s: after: md_id=%d, mdstat=0x%x, pdstat=0x%x\n", __func__, md_id, mdstat, pdstat);
152+
}
153+
154+
void am62l_save_state()
155+
{
156+
// pll value save
157+
for(int i=0;i<10;i++){
158+
saved_state.pll_hsdiv_val[i] = mmio_read_32(MAIN_PLL0_HSDIVx(i));
159+
}
160+
saved_state.pll_hsdiv_val[10] = mmio_read_32(MAIN_PLL8_CTRL);
161+
162+
//lpsc value save
163+
for(int i=0;i<LPSC_COUNT;i++){
164+
saved_state.lpsc_value[i] = mmio_read_32(LPSC_ADDR(lpsc_id[i])) & 0x3U;
165+
}
166+
167+
saved_state.ddr_reg[0] = mmio_read_32(EMIF_CTLCFG_DENALI_CTL_168);
168+
saved_state.ddr_reg[1] = mmio_read_32(EMIF_CTLCFG_DENALI_CTL_169);
169+
saved_state.ddr_reg[2] = mmio_read_32(EMIF_CTLCFG_DENALI_CTL_167);
170+
171+
saved_state.auto_clk_gate = mmio_read_32(WKUP_CTRL_MMR_CFG5_CLKGATE_CTRL0);
172+
}
173+
174+
void am62l_restore_state()
175+
{
176+
// AUTO CLOCK GATING OFF
177+
mmio_write_32(WKUP_CTRL_MMR_CFG5_CLKGATE_CTRL0,saved_state.auto_clk_gate);
178+
179+
// LPSC
180+
for(int i=0;i<LPSC_COUNT;i++){
181+
if(saved_state.lpsc_value[lpsc_id[i]]!=0){
182+
set_main_psc_state(psc_id[i],lpsc_id[i],1,saved_state.lpsc_value[lpsc_id[i]]);
183+
}
184+
}
185+
186+
/* Restore PLL */
187+
for(int i=0;i<10;i++){
188+
mmio_write_32(MAIN_PLL0_HSDIVx(i), saved_state.pll_hsdiv_val[i]);
189+
}
190+
mmio_write_32(MAIN_PLL8_CTRL,saved_state.pll_hsdiv_val[10]);
191+
}
192+
193+
void am62l_low_latency_standby()
194+
{
195+
// MAIN_PLL0
196+
mmio_write_32(MAIN_PLL0_HSDIVx(0), (saved_state.pll_hsdiv_val[0] & ~(0xff)) | 0xf);
197+
mmio_write_32(MAIN_PLL0_HSDIVx(5), (saved_state.pll_hsdiv_val[5] & ~(0xff)) | 0x4);
198+
mmio_write_32(MAIN_PLL0_HSDIVx(6), (saved_state.pll_hsdiv_val[6] & ~(0xff)) | 0x3);
199+
mmio_write_32(MAIN_PLL0_HSDIVx(7), (saved_state.pll_hsdiv_val[7] & ~(0xff)) | 0x5);
200+
mmio_write_32(MAIN_PLL0_HSDIVx(8), (saved_state.pll_hsdiv_val[8] & ~(0xff)) | 0x27);
201+
mmio_write_32(MAIN_PLL0_HSDIVx(9), (saved_state.pll_hsdiv_val[9] & ~(0x8000)));
202+
203+
// A53 running off Bypass clock
204+
mmio_write_32(MAIN_PLL8_CTRL, saved_state.pll_hsdiv_val[10] | 0x80000000);
205+
206+
// change the LPSC values only if they are not already disabled
207+
for(int i=0;i<LPSC_COUNT;i++){
208+
if(saved_state.lpsc_value[lpsc_id[i]]!=0){
209+
set_main_psc_state(psc_id[i],lpsc_id[i],1,2);
210+
}
211+
}
212+
213+
//DDR AUTO SELF REFRESH
214+
mmio_write_32(EMIF_CTLCFG_DENALI_CTL_168,0x0000ff07);
215+
mmio_write_32(EMIF_CTLCFG_DENALI_CTL_169,0x0F0F00FF);
216+
mmio_write_32(EMIF_CTLCFG_DENALI_CTL_167,0x07074007);
217+
218+
// AUTO CLOCK GATING
219+
mmio_write_32(WKUP_CTRL_MMR_CFG5_CLKGATE_CTRL0,0);
220+
221+
return;
222+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2026, ARM Limited and Contributors. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#ifndef __LPM_STANDBY_H__
8+
#define __LPM_STANDBY_H__
9+
10+
#include <plat/common/platform.h>
11+
12+
/* power domain indices */
13+
#define PD_MPU_CLST 4
14+
#define PD_MPU_CLST_CORE_0 5
15+
#define PD_MPU_CLST_CORE_1 6
16+
17+
/* lpsc indices */
18+
#define LPSC_MAIN_MPU_CLST 38
19+
#define LPSC_MAIN_MPU_CLST_PBIST 39
20+
#define LPSC_MAIN_MPU_CLST_CORE_0 40
21+
#define LPSC_MAIN_MPU_CLST_CORE_1 41
22+
23+
#define PSC_SYNCRESETDISABLE (0x0)
24+
#define PSC_SYNCRESET (0x1)
25+
#define PSC_DISABLE (0x2)
26+
#define PSC_ENABLE (0x3)
27+
#define PSC_PD_OFF (0x0)
28+
#define PSC_PD_ON (0x1)
29+
30+
// Standby idle states
31+
#define CORE_IDLE_STATE 0x1
32+
#define LOW_LATENCY_IDLE_STATE 0x2
33+
#define HIGH_LATENCY_IDLE_STATE 0x3
34+
35+
void am62l_save_state(void);
36+
void am62l_restore_state(void);
37+
void am62l_low_latency_standby(void);
38+
void set_main_psc_state(uint32_t, uint32_t, uint32_t, uint32_t);
39+
40+
#endif /* __LPM_STANDBY_H__ */

0 commit comments

Comments
 (0)