| /* |
| * 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(); |
| |
| tpm5_restore(); |
| |
| xrdc_reinit(); |
| |
| /* Restore GPIO after xrdc_reinit, otherwise MSCs are invalid */ |
| gpio_restore(apd_gpio_ctx, APD_GPIO_CTRL_NUM); |
| |
| /* 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)); |
| } |
| } |