mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 01:24:27 +00:00

Upower will check the LPAV ownership when power off the SRAM or PS. if the LPAV owner is not APD, then the power off will return failure. Add similar checking in SCMI PD driver to skip the power off to avoid failure print causing suspend/resume not work. Signed-off-by: Ye Li <ye.li@nxp.com> Reviewed-by: Jacky Bai <ping.bai@nxp.com> Reviewed-by: Peng Fan <peng.fan@nxp.com> Change-Id: I9dc657c2277129ac90a792232f734c08fca5f997
371 lines
9.4 KiB
C
371 lines
9.4 KiB
C
/*
|
|
* Copyright 2021-2024 NXP
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
#include <inttypes.h>
|
|
#include <lib/libc/errno.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <common/debug.h>
|
|
#include <drivers/scmi.h>
|
|
#include <lib/mmio.h>
|
|
#include <lib/utils_def.h>
|
|
#include <platform_def.h>
|
|
#include <scmi.h>
|
|
|
|
#include <upower_api.h>
|
|
|
|
#define POWER_STATE_ON (0 << 30)
|
|
#define POWER_STATE_OFF (1 << 30)
|
|
|
|
extern bool is_lpav_owned_by_apd(void);
|
|
|
|
enum {
|
|
PS0 = 0,
|
|
PS1 = 1,
|
|
PS2 = 2,
|
|
PS3 = 3,
|
|
PS4 = 4,
|
|
PS5 = 5,
|
|
PS6 = 6,
|
|
PS7 = 7,
|
|
PS8 = 8,
|
|
PS9 = 9,
|
|
PS10 = 10,
|
|
PS11 = 11,
|
|
PS12 = 12,
|
|
PS13 = 13,
|
|
PS14 = 14,
|
|
PS15 = 15,
|
|
PS16 = 16,
|
|
PS17 = 17,
|
|
PS18 = 18,
|
|
PS19 = 19,
|
|
};
|
|
|
|
#define SRAM_DMA1 BIT(6)
|
|
#define SRAM_FLEXSPI2 BIT(7)
|
|
#define SRAM_USB0 BIT(10)
|
|
#define SRAM_USDHC0 BIT(11)
|
|
#define SRAM_USDHC1 BIT(12)
|
|
#define SRAM_USDHC2_USB1 BIT(13)
|
|
#define SRAM_DCNANO GENMASK_32(18, 17)
|
|
#define SRAM_EPDC GENMASK_32(20, 19)
|
|
#define SRAM_DMA2 BIT(21)
|
|
#define SRAM_GPU2D GENMASK_32(23, 22)
|
|
#define SRAM_GPU3D GENMASK_32(25, 24)
|
|
#define SRAM_HIFI4 BIT(26)
|
|
#define SRAM_ISI_BUFFER BIT(27)
|
|
#define SRAM_MIPI_CSI_FIFO BIT(28)
|
|
#define SRAM_MIPI_DSI_FIFO BIT(29)
|
|
#define SRAM_PXP BIT(30)
|
|
|
|
#define SRAM_DMA0 BIT_64(33)
|
|
#define SRAM_FLEXCAN BIT_64(34)
|
|
#define SRAM_FLEXSPI0 BIT_64(35)
|
|
#define SRAM_FLEXSPI1 BIT_64(36)
|
|
|
|
struct psw {
|
|
char *name;
|
|
uint32_t reg;
|
|
int power_state;
|
|
uint32_t count;
|
|
int flags;
|
|
};
|
|
|
|
#define ALWAYS_ON BIT(0)
|
|
|
|
static struct psw imx8ulp_psw[] = {
|
|
[PS6] = { .name = "PS6", .reg = PS6, .flags = ALWAYS_ON, .power_state = POWER_STATE_ON },
|
|
[PS7] = { .name = "PS7", .reg = PS7, .power_state = POWER_STATE_OFF },
|
|
[PS8] = { .name = "PS8", .reg = PS8, .power_state = POWER_STATE_OFF },
|
|
[PS13] = { .name = "PS13", .reg = PS13, .power_state = POWER_STATE_OFF },
|
|
[PS14] = { .name = "PS14", .reg = PS14, .flags = ALWAYS_ON, .power_state = POWER_STATE_OFF },
|
|
[PS15] = { .name = "PS15", .reg = PS15, .power_state = POWER_STATE_OFF },
|
|
[PS16] = { .name = "PS16", .reg = PS16, .flags = ALWAYS_ON, .power_state = POWER_STATE_ON },
|
|
};
|
|
|
|
struct power_domain {
|
|
char *name;
|
|
uint32_t reg;
|
|
uint32_t psw_parent;
|
|
uint32_t sram_parent;
|
|
uint64_t bits;
|
|
uint32_t power_state;
|
|
bool lpav; /* belong to lpav domain */
|
|
uint32_t sw_rst_reg; /* pcc sw reset reg offset */
|
|
};
|
|
|
|
/* The Rich OS need flow the macro */
|
|
#define IMX8ULP_PD_DMA1 0
|
|
#define IMX8ULP_PD_FLEXSPI2 1
|
|
#define IMX8ULP_PD_USB0 2
|
|
#define IMX8ULP_PD_USDHC0 3
|
|
#define IMX8ULP_PD_USDHC1 4
|
|
#define IMX8ULP_PD_USDHC2_USB1 5
|
|
#define IMX8ULP_PD_DCNANO 6
|
|
#define IMX8ULP_PD_EPDC 7
|
|
#define IMX8ULP_PD_DMA2 8
|
|
#define IMX8ULP_PD_GPU2D 9
|
|
#define IMX8ULP_PD_GPU3D 10
|
|
#define IMX8ULP_PD_HIFI4 11
|
|
#define IMX8ULP_PD_ISI 12
|
|
#define IMX8ULP_PD_MIPI_CSI 13
|
|
#define IMX8ULP_PD_MIPI_DSI 14
|
|
#define IMX8ULP_PD_PXP 15
|
|
|
|
#define IMX8ULP_PD_PS6 16
|
|
#define IMX8ULP_PD_PS7 17
|
|
#define IMX8ULP_PD_PS8 18
|
|
#define IMX8ULP_PD_PS13 19
|
|
#define IMX8ULP_PD_PS14 20
|
|
#define IMX8ULP_PD_PS15 21
|
|
#define IMX8ULP_PD_PS16 22
|
|
#define IMX8ULP_PD_MAX 23
|
|
|
|
/* LPAV peripheral PCC */
|
|
#define PCC_GPU2D (IMX_PCC5_BASE + 0xf0)
|
|
#define PCC_GPU3D (IMX_PCC5_BASE + 0xf4)
|
|
#define PCC_EPDC (IMX_PCC5_BASE + 0xcc)
|
|
#define PCC_CSI (IMX_PCC5_BASE + 0xbc)
|
|
#define PCC_PXP (IMX_PCC5_BASE + 0xd0)
|
|
|
|
#define PCC_SW_RST BIT(28)
|
|
|
|
#define PWR_DOMAIN(_name, _reg, _psw_parent, _sram_parent, \
|
|
_bits, _state, _lpav, _rst_reg) \
|
|
{ \
|
|
.name = _name, \
|
|
.reg = _reg, \
|
|
.psw_parent = _psw_parent, \
|
|
.sram_parent = _sram_parent, \
|
|
.bits = _bits, \
|
|
.power_state = _state, \
|
|
.lpav = _lpav, \
|
|
.sw_rst_reg = _rst_reg, \
|
|
}
|
|
|
|
static struct power_domain scmi_power_domains[] = {
|
|
PWR_DOMAIN("DMA1", IMX8ULP_PD_DMA1, PS6, PS6, SRAM_DMA1, POWER_STATE_OFF, false, 0U),
|
|
PWR_DOMAIN("FLEXSPI2", IMX8ULP_PD_FLEXSPI2, PS6, PS6, SRAM_FLEXSPI2, POWER_STATE_OFF, false, 0U),
|
|
PWR_DOMAIN("USB0", IMX8ULP_PD_USB0, PS6, PS6, SRAM_USB0, POWER_STATE_OFF, false, 0U),
|
|
PWR_DOMAIN("USDHC0", IMX8ULP_PD_USDHC0, PS6, PS6, SRAM_USDHC0, POWER_STATE_OFF, false, 0U),
|
|
PWR_DOMAIN("USDHC1", IMX8ULP_PD_USDHC1, PS6, PS6, SRAM_USDHC1, POWER_STATE_OFF, false, 0U),
|
|
PWR_DOMAIN("USDHC2_USB1", IMX8ULP_PD_USDHC2_USB1, PS6, PS6, SRAM_USDHC2_USB1, POWER_STATE_OFF, false, 0U),
|
|
PWR_DOMAIN("DCNano", IMX8ULP_PD_DCNANO, PS16, PS16, SRAM_DCNANO, POWER_STATE_OFF, true, 0U),
|
|
PWR_DOMAIN("EPDC", IMX8ULP_PD_EPDC, PS13, PS13, SRAM_EPDC, POWER_STATE_OFF, true, PCC_EPDC),
|
|
PWR_DOMAIN("DMA2", IMX8ULP_PD_DMA2, PS16, PS16, SRAM_DMA2, POWER_STATE_OFF, true, 0U),
|
|
PWR_DOMAIN("GPU2D", IMX8ULP_PD_GPU2D, PS16, PS16, SRAM_GPU2D, POWER_STATE_OFF, true, PCC_GPU2D),
|
|
PWR_DOMAIN("GPU3D", IMX8ULP_PD_GPU3D, PS7, PS7, SRAM_GPU3D, POWER_STATE_OFF, true, PCC_GPU3D),
|
|
PWR_DOMAIN("HIFI4", IMX8ULP_PD_HIFI4, PS8, PS8, SRAM_HIFI4, POWER_STATE_OFF, true, 0U),
|
|
PWR_DOMAIN("ISI", IMX8ULP_PD_ISI, PS16, PS16, SRAM_ISI_BUFFER, POWER_STATE_OFF, true, 0U),
|
|
PWR_DOMAIN("MIPI_CSI", IMX8ULP_PD_MIPI_CSI, PS15, PS16, SRAM_MIPI_CSI_FIFO, POWER_STATE_OFF, true, PCC_CSI),
|
|
PWR_DOMAIN("MIPI_DSI", IMX8ULP_PD_MIPI_DSI, PS14, PS16, SRAM_MIPI_DSI_FIFO, POWER_STATE_OFF, true, 0U),
|
|
PWR_DOMAIN("PXP", IMX8ULP_PD_PXP, PS13, PS13, SRAM_PXP | SRAM_EPDC, POWER_STATE_OFF, true, PCC_PXP)
|
|
};
|
|
|
|
size_t plat_scmi_pd_count(unsigned int agent_id __unused)
|
|
{
|
|
return ARRAY_SIZE(scmi_power_domains);
|
|
}
|
|
|
|
const char *plat_scmi_pd_get_name(unsigned int agent_id __unused,
|
|
unsigned int pd_id)
|
|
{
|
|
if (pd_id >= IMX8ULP_PD_PS6) {
|
|
return imx8ulp_psw[pd_id - IMX8ULP_PD_PS6].name;
|
|
}
|
|
|
|
return scmi_power_domains[pd_id].name;
|
|
}
|
|
|
|
unsigned int plat_scmi_pd_get_state(unsigned int agent_id __unused,
|
|
unsigned int pd_id __unused)
|
|
{
|
|
if (pd_id >= IMX8ULP_PD_PS6) {
|
|
return imx8ulp_psw[pd_id - IMX8ULP_PD_PS6].power_state;
|
|
}
|
|
|
|
return scmi_power_domains[pd_id].power_state;
|
|
}
|
|
|
|
extern void upower_wait_resp(void);
|
|
int upwr_pwm_power(const uint32_t swton[], const uint32_t memon[], bool on)
|
|
{
|
|
int ret_val;
|
|
int ret;
|
|
|
|
if (on == true) {
|
|
ret = upwr_pwm_power_on(swton, memon, NULL);
|
|
} else {
|
|
ret = upwr_pwm_power_off(swton, memon, NULL);
|
|
}
|
|
|
|
if (ret != 0U) {
|
|
WARN("%s failed: ret: %d, state: %x\n", __func__, ret, on);
|
|
return ret;
|
|
}
|
|
|
|
upower_wait_resp();
|
|
|
|
ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000);
|
|
if (ret != UPWR_REQ_OK) {
|
|
WARN("Failure %d, %s\n", ret, __func__);
|
|
if (ret == UPWR_REQ_BUSY) {
|
|
return -EBUSY;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t plat_scmi_pd_psw(unsigned int index, unsigned int state)
|
|
{
|
|
uint32_t psw_parent = scmi_power_domains[index].psw_parent;
|
|
uint32_t sram_parent = scmi_power_domains[index].sram_parent;
|
|
uint64_t swt;
|
|
bool on;
|
|
int ret = 0;
|
|
|
|
if ((imx8ulp_psw[psw_parent].flags & ALWAYS_ON) != 0U &&
|
|
(imx8ulp_psw[sram_parent].flags & ALWAYS_ON) != 0U) {
|
|
return 0;
|
|
}
|
|
|
|
on = (state == POWER_STATE_ON) ? true : false;
|
|
|
|
if ((imx8ulp_psw[psw_parent].flags & ALWAYS_ON) == 0U) {
|
|
swt = 1 << imx8ulp_psw[psw_parent].reg;
|
|
if (imx8ulp_psw[psw_parent].count == 0U) {
|
|
if (on == false) {
|
|
WARN("off PSW[%d] that already in off state\n", psw_parent);
|
|
ret = -EACCES;
|
|
} else {
|
|
ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on);
|
|
imx8ulp_psw[psw_parent].count++;
|
|
}
|
|
} else {
|
|
if (on == true) {
|
|
imx8ulp_psw[psw_parent].count++;
|
|
} else {
|
|
imx8ulp_psw[psw_parent].count--;
|
|
}
|
|
|
|
if (imx8ulp_psw[psw_parent].count == 0U) {
|
|
ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(imx8ulp_psw[sram_parent].flags & ALWAYS_ON) && (psw_parent != sram_parent)) {
|
|
swt = 1 << imx8ulp_psw[sram_parent].reg;
|
|
if (imx8ulp_psw[sram_parent].count == 0U) {
|
|
if (on == false) {
|
|
WARN("off PSW[%d] that already in off state\n", sram_parent);
|
|
ret = -EACCES;
|
|
} else {
|
|
ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on);
|
|
imx8ulp_psw[sram_parent].count++;
|
|
}
|
|
} else {
|
|
if (on == true) {
|
|
imx8ulp_psw[sram_parent].count++;
|
|
} else {
|
|
imx8ulp_psw[sram_parent].count--;
|
|
}
|
|
|
|
if (imx8ulp_psw[sram_parent].count == 0U) {
|
|
ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool pd_allow_power_off(unsigned int pd_id)
|
|
{
|
|
if (scmi_power_domains[pd_id].lpav) {
|
|
if (!is_lpav_owned_by_apd()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void assert_pcc_reset(unsigned int pcc)
|
|
{
|
|
/* if sw_rst_reg is valid, assert the pcc reset */
|
|
if (pcc != 0U) {
|
|
mmio_clrbits_32(pcc, PCC_SW_RST);
|
|
}
|
|
}
|
|
|
|
int32_t plat_scmi_pd_set_state(unsigned int agent_id __unused,
|
|
unsigned int flags,
|
|
unsigned int pd_id,
|
|
unsigned int state)
|
|
{
|
|
unsigned int ps_idx;
|
|
uint64_t mem;
|
|
bool on;
|
|
int ret;
|
|
|
|
if (flags != 0U || pd_id >= IMX8ULP_PD_PS6) {
|
|
return SCMI_NOT_SUPPORTED;
|
|
}
|
|
|
|
ps_idx = 0;
|
|
while (ps_idx < IMX8ULP_PD_PS6 && scmi_power_domains[ps_idx].reg != pd_id) {
|
|
ps_idx++;
|
|
}
|
|
|
|
if (ps_idx == IMX8ULP_PD_PS6) {
|
|
return SCMI_NOT_FOUND;
|
|
}
|
|
|
|
if (state == scmi_power_domains[ps_idx].power_state) {
|
|
return SCMI_SUCCESS;
|
|
}
|
|
|
|
mem = scmi_power_domains[ps_idx].bits;
|
|
on = (state == POWER_STATE_ON ? true : false);
|
|
if (on == true) {
|
|
/* Assert pcc sw reset if necessary */
|
|
assert_pcc_reset(scmi_power_domains[ps_idx].sw_rst_reg);
|
|
|
|
ret = plat_scmi_pd_psw(ps_idx, state);
|
|
if (ret != 0U) {
|
|
return SCMI_DENIED;
|
|
}
|
|
|
|
ret = upwr_pwm_power(NULL, (const uint32_t *)&mem, on);
|
|
if (ret != 0U) {
|
|
return SCMI_DENIED;
|
|
}
|
|
} else {
|
|
if (!pd_allow_power_off(ps_idx)) {
|
|
return SCMI_DENIED;
|
|
}
|
|
|
|
ret = upwr_pwm_power(NULL, (const uint32_t *)&mem, on);
|
|
if (ret != 0U) {
|
|
return SCMI_DENIED;
|
|
}
|
|
|
|
ret = plat_scmi_pd_psw(ps_idx, state);
|
|
if (ret != 0U) {
|
|
return SCMI_DENIED;
|
|
}
|
|
}
|
|
|
|
scmi_power_domains[pd_id].power_state = state;
|
|
|
|
return SCMI_SUCCESS;
|
|
}
|