blob: b484a6676fbf6f5cdb32a4a85a80dc389850b910 [file] [log] [blame]
/*
* Copyright 2021-2024 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdbool.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <plat_imx8.h>
#include <xrdc.h>
#define PCC_PR BIT(31)
#define PFD_VALID_MASK U(0x40404040)
#define S400_MU_BASE U(0x27020000)
#define S400_MU_RSR (S400_MU_BASE + 0x12c)
#define S400_MU_TRx(i) (S400_MU_BASE + 0x200 + (i) * 4)
#define S400_MU_RRx(i) (S400_MU_BASE + 0x280 + (i) * 4)
/*
* need to re-init the PLL, CGC1, PCC, CMC, XRDC, SIM, GPIO etc.
* init the PLL &PFD first, then switch the CA35 clock to PLL for
* performance consideration, restore other bus fabric clock.
*/
extern void imx8ulp_caam_init(void);
extern void upower_wait_resp(void);
extern void dram_enter_retention(void);
extern void dram_exit_retention(void);
struct plat_gic_ctx imx_gicv3_ctx;
static uint32_t cmc1_pmprot;
static uint32_t cmc1_srie;
/* TPM5: global timer */
static uint32_t tpm5[3];
static uint32_t wdog3[2];
/* CGC1 PLL2 */
uint32_t pll2[][2] = {
{0x292c0510, 0x0}, {0x292c0518, 0x0}, {0x292c051c, 0x0},
{0x292c0520, 0x0}, {0x292c0500, 0x0},
};
/* CGC1 PLL3 */
uint32_t pll3[][2] = {
{0x292c0604, 0x0}, {0x292c0608, 0x0}, {0x292c060c, 0x0},
{0x292c0610, 0x0}, {0x292c0618, 0x0}, {0x292c061c, 0x0},
{0x292c0620, 0x0}, {0x292c0624, 0x0}, {0x292c0600, 0x0},
{0x292c0614, 0x0},
};
/* CGC1 others */
uint32_t cgc1[][2] = {
{0x292c0014, 0x0}, {0x292c0034, 0x0}, {0x292c0038, 0x0},
{0x292c0108, 0x0}, {0x292c0208, 0x0}, {0x292c0700, 0x0},
{0x292c0810, 0x0}, {0x292c0900, 0x0}, {0x292c0904, 0x0},
{0x292c0908, 0x0}, {0x292c090c, 0x0}, {0x292c0a00, 0x0},
};
static uint32_t pcc3[61];
static uint32_t pcc4[32];
static uint32_t pcc5_0[33];
static uint32_t pcc5_1[][2] = {
{0x2da70084, 0x0}, {0x2da70088, 0x0}, {0x2da7008c, 0x0},
{0x2da700a0, 0x0}, {0x2da700a4, 0x0}, {0x2da700a8, 0x0},
{0x2da700ac, 0x0}, {0x2da700b0, 0x0}, {0x2da700b4, 0x0},
{0x2da700bc, 0x0}, {0x2da700c0, 0x0}, {0x2da700c8, 0x0},
{0x2da700cc, 0x0}, {0x2da700d0, 0x0}, {0x2da700f0, 0x0},
{0x2da700f4, 0x0}, {0x2da700f8, 0x0}, {0x2da70108, 0x0},
{0x2da7010c, 0x0}, {0x2da70110, 0x0}, {0x2da70114, 0x0},
};
static uint32_t cgc2[][2] = {
{0x2da60014, 0x0}, {0x2da60020, 0x0}, {0x2da6003c, 0x0},
{0x2da60040, 0x0}, {0x2da60108, 0x0}, {0x2da60208, 0x0},
{0x2da60900, 0x0}, {0x2da60904, 0x0}, {0x2da60908, 0x0},
{0x2da60910, 0x0}, {0x2da60a00, 0x0},
};
static uint32_t pll4[][2] = {
{0x2da60604, 0x0}, {0x2da60608, 0x0}, {0x2da6060c, 0x0},
{0x2da60610, 0x0}, {0x2da60618, 0x0}, {0x2da6061c, 0x0},
{0x2da60620, 0x0}, {0x2da60624, 0x0}, {0x2da60600, 0x0},
{0x2da60614, 0x0},
};
static uint32_t lpav_sim[][2] = {
{0x2da50000, 0x0}, {0x2da50004, 0x0}, {0x2da50008, 0x0},
{0x2da5001c, 0x0}, {0x2da50020, 0x0}, {0x2da50024, 0x0},
{0x2da50034, 0x0},
};
#define APD_GPIO_CTRL_NUM 2
#define LPAV_GPIO_CTRL_NUM 1
#define GPIO_CTRL_REG_NUM 8
#define GPIO_PIN_MAX_NUM 32
#define GPIO_CTX(addr, num) \
{.base = (addr), .pin_num = (num), }
struct gpio_ctx {
/* gpio base */
uintptr_t base;
/* port control */
uint32_t port_ctrl[GPIO_CTRL_REG_NUM];
/* GPIO ICR, Max 32 */
uint32_t pin_num;
uint32_t gpio_icr[GPIO_PIN_MAX_NUM];
};
static uint32_t gpio_ctrl_offset[GPIO_CTRL_REG_NUM] = {
0xc, 0x10, 0x14, 0x18, 0x1c, 0x40, 0x54, 0x58
};
static struct gpio_ctx apd_gpio_ctx[APD_GPIO_CTRL_NUM] = {
GPIO_CTX(IMX_GPIOE_BASE, 24),
GPIO_CTX(IMX_GPIOF_BASE, 32),
};
static struct gpio_ctx lpav_gpio_ctx = GPIO_CTX(IMX_GPIOD_BASE, 24);
/* iomuxc setting */
#define IOMUXC_SECTION_NUM 8
struct iomuxc_section {
uint32_t offset;
uint32_t reg_num;
};
struct iomuxc_section iomuxc_sections[IOMUXC_SECTION_NUM] = {
{.offset = IOMUXC_PTD_PCR_BASE, .reg_num = 24},
{.offset = IOMUXC_PTE_PCR_BASE, .reg_num = 24},
{.offset = IOMUXC_PTF_PCR_BASE, .reg_num = 32},
{.offset = IOMUXC_PSMI_BASE0, .reg_num = 10},
{.offset = IOMUXC_PSMI_BASE1, .reg_num = 61},
{.offset = IOMUXC_PSMI_BASE2, .reg_num = 12},
{.offset = IOMUXC_PSMI_BASE3, .reg_num = 20},
{.offset = IOMUXC_PSMI_BASE4, .reg_num = 75},
};
static uint32_t iomuxc_ctx[258];
#define PORTS_NUM 3U
void apd_io_pad_off(void)
{
unsigned int i, j;
/* off the PTD/E/F, need to be customized based on actual user case */
for (i = 0; i < PORTS_NUM; i++) {
for (j = 0; j < iomuxc_sections[i].reg_num; j++) {
mmio_write_32(iomuxc_sections[i].offset + j * 4, 0);
}
}
/* disable the PTD compensation */
mmio_write_32(IMX_SIM1_BASE + 0x48, 0x800);
}
void iomuxc_save(void)
{
unsigned int i, j;
unsigned int index = 0U;
for (i = 0U; i < IOMUXC_SECTION_NUM; i++) {
for (j = 0U; j < iomuxc_sections[i].reg_num; j++) {
iomuxc_ctx[index++] = mmio_read_32(iomuxc_sections[i].offset + j * 4);
}
}
apd_io_pad_off();
}
void iomuxc_restore(void)
{
unsigned int i, j;
unsigned int index = 0U;
for (i = 0U; i < IOMUXC_SECTION_NUM; i++) {
for (j = 0U; j < iomuxc_sections[i].reg_num; j++) {
mmio_write_32(iomuxc_sections[i].offset + j * 4, iomuxc_ctx[index++]);
}
}
}
void gpio_save(struct gpio_ctx *ctx, int port_num)
{
unsigned int i, j;
for (i = 0U; i < port_num; i++) {
/* save the port control setting */
for (j = 0U; j < GPIO_CTRL_REG_NUM; j++) {
if (j < 4U) {
ctx->port_ctrl[j] = mmio_read_32(ctx->base + gpio_ctrl_offset[j]);
/*
* clear the permission setting to read the GPIO
* non-secure world setting.
*/
mmio_write_32(ctx->base + gpio_ctrl_offset[j], 0x0);
} else {
ctx->port_ctrl[j] = mmio_read_32(ctx->base + gpio_ctrl_offset[j]);
}
}
/* save the gpio icr setting */
for (j = 0U; j < ctx->pin_num; j++) {
ctx->gpio_icr[j] = mmio_read_32(ctx->base + 0x80 + j * 4);
}
ctx++;
}
}
void gpio_restore(struct gpio_ctx *ctx, int port_num)
{
unsigned int i, j;
for (i = 0U; i < port_num; i++) {
for (j = 0U; j < ctx->pin_num; j++)
mmio_write_32(ctx->base + 0x80 + j * 4, ctx->gpio_icr[j]);
for (j = 4U; j < GPIO_CTRL_REG_NUM; j++) {
mmio_write_32(ctx->base + gpio_ctrl_offset[j], ctx->port_ctrl[j]);
}
/* permission config retore last */
for (j = 0U; j < 4; j++) {
mmio_write_32(ctx->base + gpio_ctrl_offset[j], ctx->port_ctrl[j]);
}
ctx++;
}
}
void cgc1_save(void)
{
unsigned int i;
/* PLL2 */
for (i = 0U; i < ARRAY_SIZE(pll2); i++) {
pll2[i][1] = mmio_read_32(pll2[i][0]);
}
/* PLL3 */
for (i = 0U; i < ARRAY_SIZE(pll3); i++) {
pll3[i][1] = mmio_read_32(pll3[i][0]);
}
/* CGC1 others */
for (i = 0U; i < ARRAY_SIZE(cgc1); i++) {
cgc1[i][1] = mmio_read_32(cgc1[i][0]);
}
}
void cgc1_restore(void)
{
unsigned int i;
/* PLL2 */
for (i = 0U; i < ARRAY_SIZE(pll2); i++) {
mmio_write_32(pll2[i][0], pll2[i][1]);
}
/* wait for PLL2 lock */
while (!(mmio_read_32(pll2[4][0]) & BIT(24))) {
;
}
/* PLL3 */
for (i = 0U; i < 9U; i++) {
mmio_write_32(pll3[i][0], pll3[i][1]);
}
/* wait for PLL3 lock */
while (!(mmio_read_32(pll3[4][0]) & BIT(24))) {
;
}
/* restore the PFDs */
mmio_write_32(pll3[9][0], pll3[9][1] & ~(BIT(31) | BIT(23) | BIT(15) | BIT(7)));
mmio_write_32(pll3[9][0], pll3[9][1]);
/* wait for the PFD is stable, only need to check the enabled PFDs */
while (!(mmio_read_32(pll3[9][0]) & PFD_VALID_MASK)) {
;
}
/* CGC1 others */
for (i = 0U; i < ARRAY_SIZE(cgc1); i++) {
mmio_write_32(cgc1[i][0], cgc1[i][1]);
}
}
void tpm5_save(void)
{
tpm5[0] = mmio_read_32(IMX_TPM5_BASE + 0x10);
tpm5[1] = mmio_read_32(IMX_TPM5_BASE + 0x18);
tpm5[2] = mmio_read_32(IMX_TPM5_BASE + 0x20);
}
void tpm5_restore(void)
{
mmio_write_32(IMX_TPM5_BASE + 0x10, tpm5[0]);
mmio_write_32(IMX_TPM5_BASE + 0x18, tpm5[1]);
mmio_write_32(IMX_TPM5_BASE + 0x20, tpm5[2]);
}
void wdog3_save(void)
{
/* enable wdog3 clock */
mmio_write_32(IMX_PCC3_BASE + 0xa8, 0xd2800000);
/* save the CS & TOVAL regiter */
wdog3[0] = mmio_read_32(IMX_WDOG3_BASE);
wdog3[1] = mmio_read_32(IMX_WDOG3_BASE + 0x8);
}
void wdog3_restore(void)
{
/* enable wdog3 clock */
mmio_write_32(IMX_PCC3_BASE + 0xa8, 0xd2800000);
/* reconfig the CS */
mmio_write_32(IMX_WDOG3_BASE, wdog3[0]);
/* set the tiemout value */
mmio_write_32(IMX_WDOG3_BASE + 0x8, wdog3[1]);
/* wait for the lock status */
while ((mmio_read_32(IMX_WDOG3_BASE) & BIT(11))) {
;
}
/* wait for the config done */
while (!(mmio_read_32(IMX_WDOG3_BASE) & BIT(10))) {
;
}
}
static uint32_t lpuart_regs[4];
#define LPUART_BAUD 0x10
#define LPUART_CTRL 0x18
#define LPUART_FIFO 0x28
#define LPUART_WATER 0x2c
void lpuart_save(void)
{
lpuart_regs[0] = mmio_read_32(IMX_LPUART5_BASE + LPUART_BAUD);
lpuart_regs[1] = mmio_read_32(IMX_LPUART5_BASE + LPUART_FIFO);
lpuart_regs[2] = mmio_read_32(IMX_LPUART5_BASE + LPUART_WATER);
lpuart_regs[3] = mmio_read_32(IMX_LPUART5_BASE + LPUART_CTRL);
}
void lpuart_restore(void)
{
mmio_write_32(IMX_LPUART5_BASE + LPUART_BAUD, lpuart_regs[0]);
mmio_write_32(IMX_LPUART5_BASE + LPUART_FIFO, lpuart_regs[1]);
mmio_write_32(IMX_LPUART5_BASE + LPUART_WATER, lpuart_regs[2]);
mmio_write_32(IMX_LPUART5_BASE + LPUART_CTRL, lpuart_regs[3]);
}
bool is_lpav_owned_by_apd(void)
{
return (mmio_read_32(0x2802b044) & BIT(7)) ? true : false;
}
void lpav_ctx_save(void)
{
unsigned int i;
uint32_t val;
/* CGC2 save */
for (i = 0U; i < ARRAY_SIZE(cgc2); i++) {
cgc2[i][1] = mmio_read_32(cgc2[i][0]);
}
/* PLL4 */
for (i = 0U; i < ARRAY_SIZE(pll4); i++) {
pll4[i][1] = mmio_read_32(pll4[i][0]);
}
/* PCC5 save */
for (i = 0U; i < ARRAY_SIZE(pcc5_0); i++) {
val = mmio_read_32(IMX_PCC5_BASE + i * 4);
if (val & PCC_PR) {
pcc5_0[i] = val;
}
}
for (i = 0U; i < ARRAY_SIZE(pcc5_1); i++) {
val = mmio_read_32(pcc5_1[i][0]);
if (val & PCC_PR) {
pcc5_1[i][1] = val;
}
}
/* LPAV SIM save */
for (i = 0U; i < ARRAY_SIZE(lpav_sim); i++) {
lpav_sim[i][1] = mmio_read_32(lpav_sim[i][0]);
}
/* Save GPIO port D */
gpio_save(&lpav_gpio_ctx, LPAV_GPIO_CTRL_NUM);
/* put DDR into retention */
dram_enter_retention();
}
void lpav_ctx_restore(void)
{
unsigned int i;
/* PLL4 */
for (i = 0U; i < 9U; i++) {
mmio_write_32(pll4[i][0], pll4[i][1]);
}
/* wait for PLL4 lock */
while (!(mmio_read_32(pll4[8][0]) & BIT(24))) {
;
}
/* restore the PLL4 PFDs */
mmio_write_32(pll4[9][0], pll4[9][1] & ~(BIT(31) | BIT(23) | BIT(15) | BIT(7)));
mmio_write_32(pll4[9][0], pll4[9][1]);
/* wait for the PFD is stable */
while (!(mmio_read_32(pll4[9][0]) & PFD_VALID_MASK)) {
;
}
/* CGC2 restore */
for (i = 0U; i < ARRAY_SIZE(cgc2); i++) {
mmio_write_32(cgc2[i][0], cgc2[i][1]);
}
/* PCC5 restore */
for (i = 0U; i < ARRAY_SIZE(pcc5_0); i++) {
if (pcc5_0[i] & PCC_PR) {
mmio_write_32(IMX_PCC5_BASE + i * 4, pcc5_0[i]);
}
}
for (i = 0U; i < ARRAY_SIZE(pcc5_1); i++) {
if (pcc5_1[i][1] & PCC_PR) {
mmio_write_32(pcc5_1[i][0], pcc5_1[i][1]);
}
}
/* LPAV_SIM */
for (i = 0U; i < ARRAY_SIZE(lpav_sim); i++) {
mmio_write_32(lpav_sim[i][0], lpav_sim[i][1]);
}
gpio_restore(&lpav_gpio_ctx, LPAV_GPIO_CTRL_NUM);
/* DDR retention exit */
dram_exit_retention();
}
void imx_apd_ctx_save(unsigned int proc_num)
{
unsigned int i;
uint32_t val;
/* enable LPUART5's clock by default */
mmio_setbits_32(IMX_PCC3_BASE + 0xe8, BIT(30));
/* save the gic config */
plat_gic_save(proc_num, &imx_gicv3_ctx);
cmc1_pmprot = mmio_read_32(IMX_CMC1_BASE + 0x18);
cmc1_srie = mmio_read_32(IMX_CMC1_BASE + 0x8c);
/* save the PCC3 */
for (i = 0U; i < ARRAY_SIZE(pcc3); i++) {
/* save the pcc if it is exist */
val = mmio_read_32(IMX_PCC3_BASE + i * 4);
if (val & PCC_PR) {
pcc3[i] = val;
}
}
/* save the PCC4 */
for (i = 0U; i < ARRAY_SIZE(pcc4); i++) {
/* save the pcc if it is exist */
val = mmio_read_32(IMX_PCC4_BASE + i * 4);
if (val & PCC_PR) {
pcc4[i] = val;
}
}
/* save the CGC1 */
cgc1_save();
wdog3_save();
gpio_save(apd_gpio_ctx, APD_GPIO_CTRL_NUM);
iomuxc_save();
tpm5_save();
lpuart_save();
/*
* save the lpav ctx & put the ddr into retention
* if lpav master is assigned to APD domain.
*/
if (is_lpav_owned_by_apd()) {
lpav_ctx_save();
}
}
void xrdc_reinit(void)
{
xrdc_apply_apd_config();
xrdc_apply_lpav_config();
xrdc_enable();
}
void s400_release_caam(void)
{
uint32_t msg, resp;
mmio_write_32(S400_MU_TRx(0), 0x17d70206);
mmio_write_32(S400_MU_TRx(1), 0x7);
do {
resp = mmio_read_32(S400_MU_RSR);
} while ((resp & 0x3) != 0x3);
msg = mmio_read_32(S400_MU_RRx(0));
resp = mmio_read_32(S400_MU_RRx(1));
VERBOSE("resp %x; %x", msg, resp);
}
void imx_apd_ctx_restore(unsigned int proc_num)
{
unsigned int i;
/* restore the CCG1 */
cgc1_restore();
for (i = 0U; i < ARRAY_SIZE(pcc3); i++) {
/* save the pcc if it is exist */
if (pcc3[i] & PCC_PR) {
mmio_write_32(IMX_PCC3_BASE + i * 4, pcc3[i]);
}
}
for (i = 0U; i < ARRAY_SIZE(pcc4); i++) {
if (pcc4[i] & PCC_PR) {
mmio_write_32(IMX_PCC4_BASE + i * 4, pcc4[i]);
}
}
wdog3_restore();
iomuxc_restore();
gpio_restore(apd_gpio_ctx, APD_GPIO_CTRL_NUM);
tpm5_restore();
xrdc_reinit();
/* restore the gic config */
plat_gic_restore(proc_num, &imx_gicv3_ctx);
mmio_write_32(IMX_CMC1_BASE + 0x18, cmc1_pmprot);
mmio_write_32(IMX_CMC1_BASE + 0x8c, cmc1_srie);
/* enable LPUART5's clock by default */
mmio_setbits_32(IMX_PCC3_BASE + 0xe8, BIT(30));
/* restore the console lpuart */
lpuart_restore();
/* FIXME: make uart work for ATF */
mmio_write_32(IMX_LPUART_BASE + 0x18, 0xc0000);
/* Allow M core to reset A core */
mmio_clrbits_32(IMX_MU0B_BASE + 0x10, BIT(2));
/*
* Ask S400 to release caam to APD as it is owned by s400
*/
s400_release_caam();
/* re-init the caam */
imx8ulp_caam_init();
/*
* ack the upower, seems a necessary steps, otherwise the upower can
* not response to the new API service call. put this just before the
* ddr retention exit because that the dram retention exit flow need to
* communicate with upower.
*/
upower_wait_resp();
/*
* restore the lpav ctx & make ddr out of retention
* if lpav master is assigned to APD domain.
*/
if (is_lpav_owned_by_apd()) {
lpav_ctx_restore();
}
}
#define DGO_CTRL1 U(0xc)
#define USB_WAKEUP U(0x44)
#define USB1_PHY_DPD_WAKEUP_EN BIT_32(5)
#define USB0_PHY_DPD_WAKEUP_EN BIT_32(4)
#define USB1_PHY_WAKEUP_ISO_DISABLE BIT_32(1)
#define USB0_PHY_WAKEUP_ISO_DISABLE BIT_32(0)
void usb_wakeup_enable(bool enable)
{
if (enable) {
mmio_setbits_32(IMX_SIM1_BASE + USB_WAKEUP,
USB1_PHY_WAKEUP_ISO_DISABLE | USB0_PHY_WAKEUP_ISO_DISABLE);
mmio_setbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0));
while (!(mmio_read_32(IMX_SIM1_BASE + DGO_CTRL1) & BIT(1))) {
;
}
mmio_clrbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0));
mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(1));
/* Need to delay for a while to make sure the wakeup logic can work */
udelay(500);
mmio_setbits_32(IMX_SIM1_BASE + USB_WAKEUP,
USB1_PHY_DPD_WAKEUP_EN | USB0_PHY_DPD_WAKEUP_EN);
mmio_setbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0));
while (!(mmio_read_32(IMX_SIM1_BASE + DGO_CTRL1) & BIT(1))) {
;
}
mmio_clrbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0));
mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(1));
} else {
/*
* USBx_PHY_DPD_WAKEUP_EN should be cleared before USB0_PHY_WAKEUP_ISO_DISABLE
* to provide the correct the wake-up functionality.
*/
mmio_write_32(IMX_SIM1_BASE + USB_WAKEUP, USB1_PHY_WAKEUP_ISO_DISABLE |
USB0_PHY_WAKEUP_ISO_DISABLE);
mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0));
while (!(mmio_read_32(IMX_SIM1_BASE + DGO_CTRL1) & BIT(1))) {
;
}
mmio_clrbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0));
mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(1));
}
}