|
| 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 | +} |
0 commit comments