// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rkpm_gicv2.h" #include "rkpm_helpers.h" #include "rkpm_uart.h" #include "rockchip_hptimer.h" #include "rv1106_pm.h" #define RV1106_PM_REG_REGION_MEM_SIZE SZ_4K #define CRU_PVTPLL0_CON0_L 0x00 #define CRU_PVTPLL0_CON0_H 0x04 #define CRU_PVTPLL0_CON1_L 0x08 #define CRU_PVTPLL0_CON1_H 0x0c #define CRU_PVTPLL0_CON2_L 0x10 #define CRU_PVTPLL0_CON2_H 0x14 #define CRU_PVTPLL0_CON3_L 0x18 #define CRU_PVTPLL0_CON3_H 0x1c #define CRU_PVTPLL1_CON0_L 0x30 #define CRU_PVTPLL1_CON0_H 0x34 #define CRU_PVTPLL1_CON1_L 0x38 #define CRU_PVTPLL1_CON1_H 0x3c #define CRU_PVTPLL1_CON2_L 0x40 #define CRU_PVTPLL1_CON2_H 0x44 #define CRU_PVTPLL1_CON3_L 0x48 #define CRU_PVTPLL1_CON3_H 0x4c enum { RV1106_GPIO_PULL_NONE, RV1106_GPIO_PULL_UP, RV1106_GPIO_PULL_DOWN, RV1106_GPIO_PULL_UP_DOWN, }; struct rockchip_pm_data { const struct platform_suspend_ops *ops; int (*init)(struct device_node *np); }; struct rv1106_sleep_ddr_data { u32 cru_gate_con[RV1106_CRU_GATE_CON_NUM]; u32 pmucru_gate_con[RV1106_PMUCRU_GATE_CON_NUM]; u32 pericru_gate_con[RV1106_PERICRU_GATE_CON_NUM]; u32 npucru_gate_con[RV1106_NPUCRU_GATE_CON_NUM]; u32 venccru_gate_con[RV1106_VENCCRU_GATE_CON_NUM]; u32 vicru_gate_con[RV1106_VICRU_GATE_CON_NUM]; u32 vocru_gate_con[RV1106_VOCRU_GATE_CON_NUM]; u32 ddrgrf_con1, ddrgrf_con2, ddrgrf_con3, ddrc_pwrctrl, ddrc_dfilpcfg0; u32 pmucru_sel_con7; u32 pmugrf_soc_con0, pmugrf_soc_con1, pmugrf_soc_con4, pmugrf_soc_con5; u32 ioc0_1a_iomux_l, ioc1_1a_iomux_l; u32 gpio0a_iomux_l, gpio0a_iomux_h, gpio0a0_pull; u32 gpio0_ddr_l, gpio0_ddr_h; u32 pmu_wkup_int_st, gpio0_int_st; u32 sleep_clk_freq_hz; }; static struct rv1106_sleep_ddr_data ddr_data; static const struct rk_sleep_config *slp_cfg; static void __iomem *pmucru_base; static void __iomem *cru_base; static void __iomem *pvtpllcru_base; static void __iomem *pericru_base; static void __iomem *vicru_base; static void __iomem *npucru_base; static void __iomem *corecru_base; static void __iomem *venccru_base; static void __iomem *vocru_base; static void __iomem *perigrf_base; static void __iomem *vencgrf_base; static void __iomem *npugrf_base; static void __iomem *pmugrf_base; static void __iomem *ddrgrf_base; static void __iomem *coregrf_base; static void __iomem *vigrf_base; static void __iomem *vogrf_base; static void __iomem *perisgrf_base; static void __iomem *visgrf_base; static void __iomem *npusgrf_base; static void __iomem *coresgrf_base; static void __iomem *vencsgrf_base; static void __iomem *vosgrf_base; static void __iomem *pmusgrf_base; static void __iomem *pmupvtm_base; static void __iomem *uartdbg_base; static void __iomem *pmu_base; static void __iomem *gicd_base; static void __iomem *gicc_base; static void __iomem *hptimer_base; static void __iomem *firewall_ddr_base; static void __iomem *firewall_syssram_base; static void __iomem *pmu_base; static void __iomem *nstimer_base; static void __iomem *stimer_base; static void __iomem *wdt_ns_base; static void __iomem *wdt_s_base; static void __iomem *mbox_base; static void __iomem *ddrc_base; static void __iomem *ioc_base[5]; static void __iomem *gpio_base[5]; static void __iomem *rv1106_bootram_base; #define WMSK_VAL 0xffff0000 static struct reg_region vd_core_reg_rgns[] = { /* core_cru */ { REG_REGION(0x300, 0x310, 4, &corecru_base, WMSK_VAL)}, { REG_REGION(0x800, 0x804, 4, &corecru_base, WMSK_VAL)}, /* core_sgrf */ { REG_REGION(0x004, 0x014, 4, &coresgrf_base, 0)}, { REG_REGION(0x000, 0x000, 4, &coresgrf_base, 0)}, { REG_REGION(0x020, 0x030, 4, &coresgrf_base, WMSK_VAL)}, { REG_REGION(0x040, 0x040, 4, &coresgrf_base, WMSK_VAL)}, { REG_REGION(0x044, 0x044, 4, &coresgrf_base, 0)}, /* core grf */ { REG_REGION(0x004, 0x004, 4, &coregrf_base, WMSK_VAL)}, { REG_REGION(0x008, 0x010, 4, &coregrf_base, 0)}, { REG_REGION(0x024, 0x028, 4, &coregrf_base, 0)}, { REG_REGION(0x000, 0x000, 4, &coregrf_base, WMSK_VAL)}, { REG_REGION(0x02c, 0x02c, 4, &coregrf_base, WMSK_VAL)}, { REG_REGION(0x038, 0x03c, 4, &coregrf_base, WMSK_VAL)}, }; static struct reg_region vd_log_reg_rgns[] = { /* firewall_ddr */ { REG_REGION(0x000, 0x03c, 4, &firewall_ddr_base, 0)}, { REG_REGION(0x040, 0x06c, 4, &firewall_ddr_base, 0)}, { REG_REGION(0x0f0, 0x0f0, 4, &firewall_ddr_base, 0)}, /* firewall_sram */ { REG_REGION(0x000, 0x01c, 4, &firewall_syssram_base, 0)}, { REG_REGION(0x040, 0x054, 4, &firewall_syssram_base, 0)}, { REG_REGION(0x0f0, 0x0f0, 4, &firewall_syssram_base, 0)}, /* cru */ { REG_REGION(0x000, 0x004, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x008, 0x008, 4, &cru_base, 0)}, { REG_REGION(0x00c, 0x010, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x020, 0x024, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x028, 0x028, 4, &cru_base, 0)}, { REG_REGION(0x02c, 0x030, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x060, 0x064, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x068, 0x068, 4, &cru_base, 0)}, { REG_REGION(0x06c, 0x070, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x140, 0x1bc, 4, &cru_base, 0)}, /* { REG_REGION(0x280, 0x280, 4, &cru_base, WMSK_VAL)}, */ { REG_REGION(0x300, 0x310, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x314, 0x34c, 8, &cru_base, WMSK_VAL)}, { REG_REGION(0x318, 0x350, 8, &cru_base, 0)}, { REG_REGION(0x354, 0x360, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x364, 0x37c, 8, &cru_base, WMSK_VAL)}, { REG_REGION(0x368, 0x380, 8, &cru_base, 0)}, { REG_REGION(0x384, 0x384, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0x800, 0x80c, 4, &cru_base, WMSK_VAL)}, { REG_REGION(0xc00, 0xc00, 4, &cru_base, 0)}, { REG_REGION(0xc10, 0xc10, 4, &cru_base, 0)}, { REG_REGION(0xc14, 0xc28, 4, &cru_base, WMSK_VAL)}, /* npu_cru */ { REG_REGION(0x300, 0x300, 4, &npucru_base, WMSK_VAL)}, { REG_REGION(0x800, 0x804, 4, &npucru_base, WMSK_VAL)}, /* npu_sgrf */ { REG_REGION(0x004, 0x014, 4, &npusgrf_base, 0)}, { REG_REGION(0x000, 0x000, 4, &npusgrf_base, 0)}, /* peri_cru */ { REG_REGION(0x304, 0x32c, 4, &pericru_base, WMSK_VAL)}, { REG_REGION(0x800, 0x81c, 4, &pericru_base, WMSK_VAL)}, /* peri_grf */ { REG_REGION(0x000, 0x004, 4, &perigrf_base, WMSK_VAL)}, { REG_REGION(0x090, 0x094, 4, &perigrf_base, WMSK_VAL)}, /* peri_sgrf */ { REG_REGION(0x004, 0x014, 4, &perisgrf_base, 0)}, { REG_REGION(0x000, 0x000, 4, &perisgrf_base, 0)}, { REG_REGION(0x020, 0x030, 4, &perisgrf_base, WMSK_VAL)}, { REG_REGION(0x080, 0x0a4, 4, &perisgrf_base, WMSK_VAL)}, { REG_REGION(0x0b8, 0x0bc, 4, &perisgrf_base, WMSK_VAL)}, /* vi_cru */ { REG_REGION(0x300, 0x30c, 4, &vicru_base, WMSK_VAL)}, { REG_REGION(0x800, 0x808, 4, &vicru_base, WMSK_VAL)}, /* vi_sgrf */ { REG_REGION(0x004, 0x014, 4, &visgrf_base, 0)}, { REG_REGION(0x000, 0x000, 4, &visgrf_base, 0)}, /* vo_cru */ { REG_REGION(0x300, 0x30c, 4, &vocru_base, WMSK_VAL)}, { REG_REGION(0x800, 0x80c, 4, &vocru_base, WMSK_VAL)}, /* vo_sgrf */ { REG_REGION(0x004, 0x014, 4, &vosgrf_base, 0)}, { REG_REGION(0x000, 0x000, 4, &vosgrf_base, 0)}, { REG_REGION(0x018, 0x018, 4, &vosgrf_base, WMSK_VAL)}, /* vepu_cru */ { REG_REGION(0x300, 0x304, 4, &venccru_base, WMSK_VAL)}, { REG_REGION(0x800, 0x808, 4, &venccru_base, WMSK_VAL)}, /* vepu_sgrf */ { REG_REGION(0x004, 0x014, 4, &vencsgrf_base, 0)}, { REG_REGION(0x000, 0x000, 4, &vencsgrf_base, 0)}, /* gpio1_ioc */ { REG_REGION(0x000, 0x018, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x080, 0x0b4, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x180, 0x18c, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x1c0, 0x1cc, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x200, 0x20c, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x240, 0x24c, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x280, 0x28c, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x2c0, 0x2cc, 4, &ioc_base[1], WMSK_VAL)}, { REG_REGION(0x2f4, 0x2f4, 4, &ioc_base[1], WMSK_VAL)}, /* gpio2_ioc */ { REG_REGION(0x020, 0x028, 4, &ioc_base[2], WMSK_VAL)}, { REG_REGION(0x0c0, 0x0d0, 4, &ioc_base[2], WMSK_VAL)}, { REG_REGION(0x190, 0x194, 4, &ioc_base[2], WMSK_VAL)}, { REG_REGION(0x1d0, 0x1d4, 4, &ioc_base[2], WMSK_VAL)}, { REG_REGION(0x210, 0x214, 4, &ioc_base[2], WMSK_VAL)}, { REG_REGION(0x250, 0x254, 4, &ioc_base[2], WMSK_VAL)}, { REG_REGION(0x290, 0x294, 4, &ioc_base[2], WMSK_VAL)}, { REG_REGION(0x2d0, 0x2d4, 4, &ioc_base[2], WMSK_VAL)}, /* gpio3_ioc */ { REG_REGION(0x040, 0x058, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x100, 0x10c, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x128, 0x134, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x1a0, 0x1ac, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x1e0, 0x1ec, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x220, 0x22c, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x260, 0x26c, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x2a0, 0x2ac, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x2e0, 0x2ec, 4, &ioc_base[3], WMSK_VAL)}, { REG_REGION(0x2f4, 0x2f4, 4, &ioc_base[3], WMSK_VAL)}, /* gpio1~3 */ { REG_REGION(0x000, 0x00c, 4, &gpio_base[1], WMSK_VAL)}, { REG_REGION(0x018, 0x044, 4, &gpio_base[1], WMSK_VAL)}, { REG_REGION(0x048, 0x048, 4, &gpio_base[1], 0)}, { REG_REGION(0x060, 0x064, 4, &gpio_base[1], WMSK_VAL)}, { REG_REGION(0x100, 0x108, 4, &gpio_base[1], WMSK_VAL)}, { REG_REGION(0x010, 0x014, 4, &gpio_base[1], WMSK_VAL)}, { REG_REGION(0x000, 0x00c, 4, &gpio_base[2], WMSK_VAL)}, { REG_REGION(0x018, 0x044, 4, &gpio_base[2], WMSK_VAL)}, { REG_REGION(0x048, 0x048, 4, &gpio_base[2], 0)}, { REG_REGION(0x060, 0x064, 4, &gpio_base[2], WMSK_VAL)}, { REG_REGION(0x100, 0x108, 4, &gpio_base[2], WMSK_VAL)}, { REG_REGION(0x010, 0x014, 4, &gpio_base[2], WMSK_VAL)}, { REG_REGION(0x000, 0x00c, 4, &gpio_base[3], WMSK_VAL)}, { REG_REGION(0x018, 0x044, 4, &gpio_base[3], WMSK_VAL)}, { REG_REGION(0x048, 0x048, 4, &gpio_base[3], 0)}, { REG_REGION(0x060, 0x064, 4, &gpio_base[3], WMSK_VAL)}, { REG_REGION(0x100, 0x108, 4, &gpio_base[3], WMSK_VAL)}, { REG_REGION(0x010, 0x014, 4, &gpio_base[3], WMSK_VAL)}, /* NS TIMER 6 channel */ { REG_REGION(0x00, 0x04, 4, &nstimer_base, 0)}, { REG_REGION(0x10, 0x10, 4, &nstimer_base, 0)}, { REG_REGION(0x20, 0x24, 4, &nstimer_base, 0)}, { REG_REGION(0x30, 0x30, 4, &nstimer_base, 0)}, { REG_REGION(0x40, 0x44, 4, &nstimer_base, 0)}, { REG_REGION(0x50, 0x50, 4, &nstimer_base, 0)}, { REG_REGION(0x60, 0x64, 4, &nstimer_base, 0)}, { REG_REGION(0x70, 0x70, 4, &nstimer_base, 0)}, { REG_REGION(0x80, 0x84, 4, &nstimer_base, 0)}, { REG_REGION(0x90, 0x90, 4, &nstimer_base, 0)}, { REG_REGION(0xa0, 0xa4, 4, &nstimer_base, 0)}, { REG_REGION(0xb0, 0xb0, 4, &nstimer_base, 0)}, /* S TIMER0 2 channel */ { REG_REGION(0x00, 0x04, 4, &stimer_base, 0)}, { REG_REGION(0x10, 0x10, 4, &stimer_base, 0)}, { REG_REGION(0x20, 0x24, 4, &stimer_base, 0)}, { REG_REGION(0x30, 0x30, 4, &stimer_base, 0)}, /* wdt_ns */ { REG_REGION(0x04, 0x04, 4, &wdt_ns_base, 0)}, { REG_REGION(0x00, 0x00, 4, &wdt_ns_base, 0)}, /* wdt_s */ { REG_REGION(0x04, 0x04, 4, &wdt_s_base, 0)}, { REG_REGION(0x00, 0x00, 4, &wdt_s_base, 0)}, }; static int is_rv1103, is_rv1106; #define PLL_LOCKED_TIMEOUT 600000U static void pm_pll_wait_lock(u32 pll_id) { int delay = PLL_LOCKED_TIMEOUT; if (readl_relaxed(cru_base + RV1106_CRU_PLL_CON(pll_id, 1)) & CRU_PLLCON1_PWRDOWN) return; while (delay-- >= 0) { if (readl_relaxed(cru_base + RV1106_CRU_PLL_CON(pll_id, 1)) & CRU_PLLCON1_LOCK_STATUS) break; rkpm_raw_udelay(1); } if (delay <= 0) { rkpm_printstr("Can't wait pll lock: "); rkpm_printhex(pll_id); rkpm_printch('\n'); } } struct plat_gicv2_dist_ctx_t gicd_ctx_save; struct plat_gicv2_cpu_ctx_t gicc_ctx_save; static void gic400_save(void) { rkpm_gicv2_cpu_save(gicd_base, gicc_base, &gicc_ctx_save); rkpm_gicv2_dist_save(gicd_base, &gicd_ctx_save); } static void gic400_restore(void) { if (IS_ENABLED(CONFIG_RV1106_HPMCU_FAST_WAKEUP)) writel_relaxed(0x3, gicd_base + GIC_DIST_CTRL); else rkpm_gicv2_dist_restore(gicd_base, &gicd_ctx_save); rkpm_gicv2_cpu_restore(gicd_base, gicc_base, &gicc_ctx_save); } static void uart_wrtie_byte(uint8_t byte) { writel_relaxed(byte, uartdbg_base + 0x0); while (!(readl_relaxed(uartdbg_base + 0x14) & 0x40)) ; } void rkpm_printch(int c) { if (c == '\n') uart_wrtie_byte('\r'); uart_wrtie_byte(c); } #define RV1106_DUMP_GPIO_INTEN(id) \ do { \ rkpm_printstr("GPIO"); \ rkpm_printdec(id); \ rkpm_printstr(": "); \ rkpm_printhex(readl_relaxed(gpio_base[id] + RV1106_GPIO_INT_EN_L)); \ rkpm_printch(' '); \ rkpm_printhex(readl_relaxed(gpio_base[id] + RV1106_GPIO_INT_EN_H)); \ rkpm_printch(' '); \ rkpm_printhex(readl_relaxed(gpio_base[id] + RV1106_GPIO_INT_MASK_L)); \ rkpm_printch(' '); \ rkpm_printhex(readl_relaxed(gpio_base[id] + RV1106_GPIO_INT_MASK_H)); \ rkpm_printch(' '); \ rkpm_printhex(readl_relaxed(gpio_base[id] + RV1106_GPIO_INT_STATUS)); \ rkpm_printch(' '); \ rkpm_printhex(readl_relaxed(gpio_base[id] + RV1106_GPIO_INT_RAWSTATUS));\ rkpm_printch('\n'); \ } while (0) static void rv1106_dbg_pmu_wkup_src(void) { u32 pmu_int_st = ddr_data.pmu_wkup_int_st; rkpm_printstr("wake up status:"); rkpm_printhex(pmu_int_st); rkpm_printch('\n'); if (pmu_int_st) rkpm_printstr("wake up information:\n"); if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_GPIO_INT_EN)) { rkpm_printstr("GPIO0 interrupt wakeup:"); rkpm_printhex(ddr_data.gpio0_int_st); rkpm_printch('\n'); } if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_SDMMC_EN)) rkpm_printstr("PWM detect wakeup\n"); if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_SDIO_EN)) rkpm_printstr("GMAC interrupt wakeup\n"); if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_TIMER_EN)) rkpm_printstr("TIMER interrupt wakeup\n"); if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_USBDEV_EN)) rkpm_printstr("USBDEV detect wakeup\n"); if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_TIMEOUT_EN)) rkpm_printstr("TIMEOUT interrupt wakeup\n"); rkpm_printch('\n'); } static void rv1106_dbg_irq_prepare(void) { RV1106_DUMP_GPIO_INTEN(0); } static void rv1106_dbg_irq_finish(void) { rv1106_dbg_pmu_wkup_src(); } static inline u32 rv1106_l2_config(void) { u32 l2ctlr; asm("mrc p15, 1, %0, c9, c0, 2" : "=r" (l2ctlr)); return l2ctlr; } static void __init rv1106_config_bootdata(void) { rkpm_bootdata_cpusp = RV1106_PMUSRAM_BASE + (SZ_8K - 8); rkpm_bootdata_cpu_code = __pa_symbol(cpu_resume); rkpm_bootdata_l2ctlr_f = 1; rkpm_bootdata_l2ctlr = rv1106_l2_config(); } static void writel_clrset_bits(u32 clr, u32 set, void __iomem *addr) { u32 val = readl_relaxed(addr); val &= ~clr; val |= set; writel_relaxed(val, addr); } static void gic_irq_en(int irq) { writel_clrset_bits(0xff << irq % 4 * 8, 0x1 << irq % 4 * 8, gicd_base + GIC_DIST_TARGET + (irq >> 2 << 2)); writel_clrset_bits(0xff << irq % 4 * 8, 0xa0 << irq % 4 * 8, gicd_base + GIC_DIST_PRI + (irq >> 2 << 2)); writel_clrset_bits(0x3 << irq % 16 * 2, 0x1 << irq % 16 * 2, gicd_base + GIC_DIST_CONFIG + (irq >> 4 << 2)); writel_clrset_bits(BIT(irq % 32), BIT(irq % 32), gicd_base + GIC_DIST_IGROUP + (irq >> 5 << 2)); dsb(sy); writel_relaxed(0x1 << irq % 32, gicd_base + GIC_DIST_ENABLE_SET + (irq >> 5 << 2)); dsb(sy); } static int is_hpmcu_mbox_int(void) { return !!(readl(mbox_base + RV1106_MBOX_B2A_STATUS) & BIT(0)); } static void hpmcu_start(void) { /* enable hpmcu mailbox AP irq */ gic_irq_en(RV1106_HPMCU_MBOX_IRQ_AP); /* tell hpmcu that we are currently in system wake up. */ writel(RV1106_SYS_IS_WKUP, pmu_base + RV1106_PMU_SYS_REG(0)); /* set the mcu uncache area, usually set the devices address */ writel(0xff000, coregrf_base + RV1106_COREGRF_CACHE_PERI_ADDR_START); writel(0xffc00, coregrf_base + RV1106_COREGRF_CACHE_PERI_ADDR_END); /* Reset the hp mcu */ writel(0x1e001e, corecru_base + RV1106_COERCRU_SFTRST_CON(1)); /* set the mcu addr */ writel(RV1106_HPMCU_BOOT_ADDR, coresgrf_base + RV1106_CORESGRF_HPMCU_BOOTADDR); dsb(sy); /* release the mcu */ writel(0x1e0000, corecru_base + RV1106_COERCRU_SFTRST_CON(1)); dsb(sy); } static int hpmcu_fast_wkup(void) { u32 cmd; hpmcu_start(); while (1) { rkpm_printstr("-s-\n"); dsb(sy); wfi(); rkpm_printstr("-w-\n"); if (is_hpmcu_mbox_int()) { rkpm_printstr("-h-mbox-\n"); /* clear system wake up state */ writel(0, pmu_base + RV1106_PMU_SYS_REG(0)); writel(BIT(0), mbox_base + RV1106_MBOX_B2A_STATUS); break; } } cmd = readl(mbox_base + RV1106_MBOX_B2A_CMD_0); if (cmd == RV1106_MBOX_CMD_AP_SUSPEND) return 1; else return 0; } static void clock_suspend(void) { int i; for (i = 0; i < RV1106_CRU_GATE_CON_NUM; i++) { ddr_data.cru_gate_con[i] = readl_relaxed(cru_base + RV1106_CRU_GATE_CON(i)); writel_relaxed(0xffff0000, cru_base + RV1106_CRU_GATE_CON(i)); } for (i = 0; i < RV1106_PMUCRU_GATE_CON_NUM; i++) { ddr_data.pmucru_gate_con[i] = readl_relaxed(pmucru_base + RV1106_PMUCRU_GATE_CON(i)); writel_relaxed(0xffff0000, pmucru_base + RV1106_PMUCRU_GATE_CON(i)); } for (i = 0; i < RV1106_PERICRU_GATE_CON_NUM; i++) { ddr_data.pericru_gate_con[i] = readl_relaxed(pericru_base + RV1106_PERICRU_GATE_CON(i)); writel_relaxed(0xffff0000, pericru_base + RV1106_PERICRU_GATE_CON(i)); } for (i = 0; i < RV1106_NPUCRU_GATE_CON_NUM; i++) { ddr_data.npucru_gate_con[i] = readl_relaxed(npucru_base + RV1106_NPUCRU_GATE_CON(i)); writel_relaxed(0xffff0000, npucru_base + RV1106_NPUCRU_GATE_CON(i)); } for (i = 0; i < RV1106_VENCCRU_GATE_CON_NUM; i++) { ddr_data.venccru_gate_con[i] = readl_relaxed(venccru_base + RV1106_VENCCRU_GATE_CON(i)); writel_relaxed(0xffff0000, venccru_base + RV1106_VENCCRU_GATE_CON(i)); } for (i = 0; i < RV1106_VICRU_GATE_CON_NUM; i++) { ddr_data.vicru_gate_con[i] = readl_relaxed(vicru_base + RV1106_VICRU_GATE_CON(i)); writel_relaxed(0xffff0000, vicru_base + RV1106_VICRU_GATE_CON(i)); } for (i = 0; i < RV1106_VOCRU_GATE_CON_NUM; i++) { ddr_data.vocru_gate_con[i] = readl_relaxed(vocru_base + RV1106_VOCRU_GATE_CON(i)); writel_relaxed(0xffff0000, vocru_base + RV1106_VOCRU_GATE_CON(i)); } } static void clock_resume(void) { int i; for (i = 0; i < RV1106_CRU_GATE_CON_NUM; i++) writel_relaxed(WITH_16BITS_WMSK(ddr_data.cru_gate_con[i]), cru_base + RV1106_CRU_GATE_CON(i)); for (i = 0; i < RV1106_PMUCRU_GATE_CON_NUM; i++) writel_relaxed(WITH_16BITS_WMSK(ddr_data.pmucru_gate_con[i]), pmucru_base + RV1106_PMUCRU_GATE_CON(i)); for (i = 0; i < RV1106_PERICRU_GATE_CON_NUM; i++) writel_relaxed(WITH_16BITS_WMSK(ddr_data.pericru_gate_con[i]), pericru_base + RV1106_PERICRU_GATE_CON(i)); for (i = 0; i < RV1106_NPUCRU_GATE_CON_NUM; i++) writel_relaxed(WITH_16BITS_WMSK(ddr_data.npucru_gate_con[i]), npucru_base + RV1106_NPUCRU_GATE_CON(i)); for (i = 0; i < RV1106_VENCCRU_GATE_CON_NUM; i++) writel_relaxed(WITH_16BITS_WMSK(ddr_data.venccru_gate_con[i]), venccru_base + RV1106_VENCCRU_GATE_CON(i)); for (i = 0; i < RV1106_VICRU_GATE_CON_NUM; i++) writel_relaxed(WITH_16BITS_WMSK(ddr_data.vicru_gate_con[i]), vicru_base + RV1106_VICRU_GATE_CON(i)); for (i = 0; i < RV1106_VOCRU_GATE_CON_NUM; i++) writel_relaxed(WITH_16BITS_WMSK(ddr_data.vocru_gate_con[i]), vocru_base + RV1106_VOCRU_GATE_CON(i)); } static void pvtm_32k_config(void) { u64 value, pvtm_freq_hz; int pvtm_div; u32 pvtm_div_freq_hz; ddr_data.pmucru_sel_con7 = readl_relaxed(pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); if (slp_cfg->mode_config & RKPM_SLP_32K_EXT) { writel_relaxed(BITS_WITH_WMASK(0x3, 0x3, 14), pmugrf_base + RV1106_PMUGRF_SOC_CON(1)); writel_relaxed(BITS_WITH_WMASK(0x1, 0x3, 0), pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); ddr_data.sleep_clk_freq_hz = 32768; } else { writel_relaxed(BITS_WITH_WMASK(0, 0x3, 0), pmupvtm_base + RV1106_PVTM_CON(2)); writel_relaxed(RV1106_PVTM_CALC_CNT, pmupvtm_base + RV1106_PVTM_CON(1)); writel_relaxed(BITS_WITH_WMASK(0, 0x1, PVTM_START), pmupvtm_base + RV1106_PVTM_CON(0)); dsb(); writel_relaxed(BITS_WITH_WMASK(0, 0x7, PVTM_OSC_SEL), pmupvtm_base + RV1106_PVTM_CON(0)); writel_relaxed(BITS_WITH_WMASK(1, 0x1, PVTM_OSC_EN), pmupvtm_base + RV1106_PVTM_CON(0)); writel_relaxed(BITS_WITH_WMASK(1, 0x1, PVTM_RND_SEED_EN), pmupvtm_base + RV1106_PVTM_CON(0)); dsb(); writel_relaxed(BITS_WITH_WMASK(1, 0x1, PVTM_START), pmupvtm_base + RV1106_PVTM_CON(0)); dsb(); while (readl_relaxed(pmupvtm_base + RV1106_PVTM_STATUS(1)) < 30) ; dsb(); while (!readl_relaxed(pmupvtm_base + RV1106_PVTM_STATUS(0)) & 0x1) ; value = (readl_relaxed(pmupvtm_base + RV1106_PVTM_STATUS(1))); pvtm_freq_hz = (value * 24000000 + RV1106_PVTM_CALC_CNT / 2); pvtm_freq_hz = div_u64(pvtm_freq_hz, RV1106_PVTM_CALC_CNT); pvtm_div = ((u32)pvtm_freq_hz + RV1106_PVTM_TARGET_FREQ / 2) / RV1106_PVTM_TARGET_FREQ - 1; if (pvtm_div > 0xfff) pvtm_div = 0xfff; writel_relaxed(WITH_16BITS_WMSK(pvtm_div), pmugrf_base + RV1106_PMUGRF_SOC_CON(3)); /* select 32k source */ writel_relaxed(BITS_WITH_WMASK(0x2, 0x3, 0), pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); pvtm_div_freq_hz = (u32)pvtm_freq_hz / (pvtm_div + 1); ddr_data.sleep_clk_freq_hz = pvtm_div_freq_hz; rkpm_printstr("pvtm freq (hz):"); rkpm_printdec(pvtm_freq_hz); rkpm_printch('-'); rkpm_printdec(pvtm_div_freq_hz); rkpm_printch('\n'); } rkpm_printstr("sleep freq (hz):"); rkpm_printdec(ddr_data.sleep_clk_freq_hz); rkpm_printch('\n'); } static void pvtm_32k_config_restore(void) { writel_relaxed(WITH_16BITS_WMSK(ddr_data.pmucru_sel_con7), pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); if (rk_hptimer_get_mode(hptimer_base) == RK_HPTIMER_SOFT_ADJUST_MODE) rk_hptimer_do_soft_adjust_no_wait(hptimer_base, 24000000, ddr_data.sleep_clk_freq_hz); } static void ddr_sleep_config(void) { u32 val; ddr_data.ddrc_pwrctrl = readl_relaxed(ddrc_base + 0x30); ddr_data.ddrgrf_con1 = readl_relaxed(ddrgrf_base + RV1106_DDRGRF_CON(1)); ddr_data.ddrgrf_con2 = readl_relaxed(ddrgrf_base + RV1106_DDRGRF_CON(2)); ddr_data.ddrgrf_con3 = readl_relaxed(ddrgrf_base + RV1106_DDRGRF_CON(3)); ddr_data.ddrc_dfilpcfg0 = readl_relaxed(ddrc_base + 0x198); ddr_data.pmugrf_soc_con0 = readl_relaxed(pmugrf_base + RV1106_PMUGRF_SOC_CON(0)); val = readl_relaxed(ddrc_base + 0x30); writel_relaxed(val & ~(BIT(0) | BIT(1)), ddrc_base + 0x30); /* disable ddr auto gt */ writel_relaxed(BITS_WITH_WMASK(0x12, 0x1f, 0), ddrgrf_base + RV1106_DDRGRF_CON(1)); writel_relaxed(BITS_WITH_WMASK(0x3ff, 0x3ff, 0), ddrgrf_base + RV1106_DDRGRF_CON(2)); /* ddr low power request by pmu */ writel_relaxed(BITS_WITH_WMASK(0x3, 0x3, 8), ddrgrf_base + RV1106_DDRGRF_CON(3)); while ((readl_relaxed(ddrc_base + 0x4) & 0x7) != 0x1) continue; val = readl_relaxed(ddrc_base + 0x198) & ~(0xf << 12 | 0xf << 4); val |= (0xa << 12 | 0xa << 4); writel_relaxed(val, ddrc_base + 0x198); val = readl_relaxed(ddrc_base + 0x198) | BIT(8) | BIT(0); writel_relaxed(val, ddrc_base + 0x198); /* ddr io ret by pmu */ writel_relaxed(BITS_WITH_WMASK(0x0, 0x7, 9), pmugrf_base + RV1106_PMUGRF_SOC_CON(0)); } static void ddr_sleep_config_restore(void) { writel_relaxed(WITH_16BITS_WMSK(ddr_data.ddrgrf_con3), ddrgrf_base + RV1106_DDRGRF_CON(3)); } static void pmu_sleep_config(void) { u32 clk_freq_khz = 32; u32 pmu_wkup_con, pmu_pwr_con, pmu_scu_con, pmu_ddr_con; u32 pmu_bus_idle_con, pmu_cru_con[2], pmu_pll_con; ddr_data.pmugrf_soc_con1 = readl_relaxed(pmugrf_base + RV1106_PMUGRF_SOC_CON(1)); ddr_data.pmugrf_soc_con4 = readl_relaxed(pmugrf_base + RV1106_PMUGRF_SOC_CON(4)); ddr_data.pmugrf_soc_con5 = readl_relaxed(pmugrf_base + RV1106_PMUGRF_SOC_CON(5)); ddr_data.ioc1_1a_iomux_l = readl_relaxed(ioc_base[1] + 0); pmu_wkup_con = /* BIT(RV1106_PMU_WAKEUP_CPU_INT_EN) | */ BIT(RV1106_PMU_WAKEUP_GPIO_INT_EN) | 0; if (IS_ENABLED(CONFIG_RV1106_PMU_WAKEUP_TIMEOUT)) pmu_wkup_con |= BIT(RV1106_PMU_WAKEUP_TIMEOUT_EN); pmu_pwr_con = BIT(RV1106_PMU_PWRMODE_EN) | /* BIT(RV1106_PMU_BUS_BYPASS) | */ /* BIT(RV1106_PMU_DDR_BYPASS) | */ /* BIT(RV1106_PMU_CRU_BYPASS) | */ 0; pmu_scu_con = BIT(RV1106_PMU_SCU_INT_MASK_ENA) | BIT(RV1106_PMU_CPU_INT_MASK_ENA) | 0; pmu_bus_idle_con = BIT(RV1106_PMU_IDLE_REQ_MSCH) | BIT(RV1106_PMU_IDLE_REQ_DDR) | BIT(RV1106_PMU_IDLE_REQ_NPU) | BIT(RV1106_PMU_IDLE_REQ_NPU_ACLK) | BIT(RV1106_PMU_IDLE_REQ_VI) | BIT(RV1106_PMU_IDLE_REQ_VO) | BIT(RV1106_PMU_IDLE_REQ_PERI) | BIT(RV1106_PMU_IDLE_REQ_CRU) | BIT(RV1106_PMU_IDLE_REQ_CPU) | BIT(RV1106_PMU_IDLE_REQ_VENC_COM) | BIT(RV1106_PMU_IDLE_REQ_VEPU) | 0; pmu_cru_con[0] = BIT(RV1106_PMU_ALIVE_32K_ENA) | BIT(RV1106_PMU_OSC_DIS_ENA) | BIT(RV1106_PMU_WAKEUP_RST_ENA) | BIT(RV1106_PMU_INPUT_CLAMP_ENA) | /* BIT(RV1106_PMU_ALIVE_OSC_ENA) | */ BIT(RV1106_PMU_POWER_OFF_ENA) | 0; pmu_cru_con[1] = /* BIT(RV1106_PMU_VI_CLK_SRC_GATE_ENA) | */ /* BIT(RV1106_PMU_VO_CLK_SRC_GATE_ENA) | */ /* BIT(RV1106_PMU_VENC_CLK_SRC_GATE_ENA) | */ /* BIT(RV1106_PMU_NPU_CLK_SRC_GATE_ENA) | */ /* BIT(RV1106_PMU_DDR_CLK_SRC_CATE_ENA) | */ /* BIT(RV1106_PMU_PERI_CLK_SRC_GATE_ENA) | */ /* BIT(RV1106_PMU_CORE_CLK_SRC_GATE_ENA) | */ /* BIT(RV1106_PMU_CRU_CLK_SRC_GATE_ENA) | */ 0; pmu_ddr_con = BIT(RV1106_PMU_DDR_SREF_C_ENA) | #if !RV1106_WAKEUP_TO_SYSTEM_RESET BIT(RV1106_PMU_DDRIO_RET_ENA) | #endif BIT(RV1106_PMU_DDRCTL_C_AUTO_GATING_ENA) | BIT(RV1106_PMU_MSCH_AUTO_GATING_ENA) | BIT(RV1106_PMU_DDR_SREF_A_ENA) | BIT(RV1106_PMU_DDRCTL_A_AUTO_GATING_ENA) | 0; pmu_pll_con = BIT(RV1106_PMU_APLL_PD_ENA) | BIT(RV1106_PMU_DPLL_PD_ENA) | BIT(RV1106_PMU_CPLL_PD_ENA) | BIT(RV1106_PMU_GPLL_PD_ENA) | 0; /* pmu_debug */ writel_relaxed(0xffffff01, pmu_base + RV1106_PMU_INFO_TX_CON); writel_relaxed(BITS_WITH_WMASK(0x1, 0xf, 4), ioc_base[1] + 0); /* pmu count */ writel_relaxed(clk_freq_khz * 10, pmu_base + RV1106_PMU_OSC_STABLE_CNT); writel_relaxed(clk_freq_khz * 5, pmu_base + RV1106_PMU_PMIC_STABLE_CNT); /* Pmu's clk has switched to 24M back When pmu FSM counts * the follow counters, so we should use 24M to calculate * these counters. */ writel_relaxed(12000, pmu_base + RV1106_PMU_WAKEUP_RSTCLR_CNT); writel_relaxed(12000, pmu_base + RV1106_PMU_PLL_LOCK_CNT); writel_relaxed(24000 * 2, pmu_base + RV1106_PMU_PWM_SWITCH_CNT); /* pmu reset hold */ writel_relaxed(0xffffffff, pmugrf_base + RV1106_PMUGRF_SOC_CON(4)); writel_relaxed(0xffffff47, pmugrf_base + RV1106_PMUGRF_SOC_CON(5)); writel_relaxed(0x00010001, pmu_base + RV1106_PMU_INT_MASK_CON); writel_relaxed(WITH_16BITS_WMSK(pmu_scu_con), pmu_base + RV1106_PMU_SCU_PWR_CON); writel_relaxed(WITH_16BITS_WMSK(pmu_cru_con[0]), pmu_base + RV1106_PMU_CRU_PWR_CON0); writel_relaxed(WITH_16BITS_WMSK(pmu_cru_con[1]), pmu_base + RV1106_PMU_CRU_PWR_CON1); writel_relaxed(WITH_16BITS_WMSK(pmu_bus_idle_con), pmu_base + RV1106_PMU_BIU_IDLE_CON); writel_relaxed(WITH_16BITS_WMSK(pmu_ddr_con), pmu_base + RV1106_PMU_DDR_PWR_CON); writel_relaxed(WITH_16BITS_WMSK(pmu_pll_con), pmu_base + RV1106_PMU_PLLPD_CON); writel_relaxed(pmu_wkup_con, pmu_base + RV1106_PMU_WAKEUP_INT_CON); writel_relaxed(WITH_16BITS_WMSK(pmu_pwr_con), pmu_base + RV1106_PMU_PWR_CON); #if RV1106_WAKEUP_TO_SYSTEM_RESET writel_relaxed(0, pmugrf_base + RV1106_PMUGRF_OS_REG(9)); /* Use PMUGRF_OS_REG10 to save wakeup source */ writel_relaxed(0, pmugrf_base + RV1106_PMUGRF_OS_REG(10)); #else writel_relaxed(PMU_SUSPEND_MAGIC, pmugrf_base + RV1106_PMUGRF_OS_REG(9)); #endif } static void pmu_sleep_restore(void) { ddr_data.pmu_wkup_int_st = readl_relaxed(pmu_base + RV1106_PMU_WAKEUP_INT_ST); ddr_data.gpio0_int_st = readl_relaxed(gpio_base[0] + RV1106_GPIO_INT_STATUS); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_INFO_TX_CON); writel_relaxed(0x00010000, pmu_base + RV1106_PMU_INT_MASK_CON); writel_relaxed(0x00000000, pmu_base + RV1106_PMU_WAKEUP_INT_CON); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_PWR_CON); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_BIU_IDLE_CON); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_DDR_PWR_CON); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_SCU_PWR_CON); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_PLLPD_CON); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_CRU_PWR_CON0); writel_relaxed(0xffff0000, pmu_base + RV1106_PMU_CRU_PWR_CON1); writel_relaxed(WITH_16BITS_WMSK(ddr_data.ioc1_1a_iomux_l), ioc_base[1] + 0); writel_relaxed(WITH_16BITS_WMSK(ddr_data.pmugrf_soc_con1), pmugrf_base + RV1106_PMUGRF_SOC_CON(1)); writel_relaxed(WITH_16BITS_WMSK(ddr_data.pmugrf_soc_con4), pmugrf_base + RV1106_PMUGRF_SOC_CON(4)); writel_relaxed(WITH_16BITS_WMSK(ddr_data.pmugrf_soc_con5), pmugrf_base + RV1106_PMUGRF_SOC_CON(5)); } static void soc_sleep_config(void) { ddr_data.ioc0_1a_iomux_l = readl_relaxed(ioc_base[0] + 0); rkpm_printch('a'); pvtm_32k_config(); rkpm_printch('b'); ddr_sleep_config(); rkpm_printch('c'); pmu_sleep_config(); rkpm_printch('d'); } static void soc_sleep_restore(void) { rkpm_printch('d'); pmu_sleep_restore(); rkpm_printch('c'); ddr_sleep_config_restore(); rkpm_printch('b'); pvtm_32k_config_restore(); rkpm_printch('a'); writel_relaxed(WITH_16BITS_WMSK(ddr_data.ioc0_1a_iomux_l), ioc_base[0] + 0); } static void plls_suspend(void) { } static void plls_resume(void) { } static void gpio0_set_iomux(u32 pin_id, u32 func) { u32 sft = (pin_id % 4) << 2; if (pin_id < 4) writel_relaxed(BITS_WITH_WMASK(func, 0xf, sft), ioc_base[0] + 0); else writel_relaxed(BITS_WITH_WMASK(func, 0xf, sft), ioc_base[0] + 4); } static void gpio0_set_pull(u32 pin_id, int pull) { u32 sft = (pin_id % 8) << 1; writel_relaxed(BITS_WITH_WMASK(pull, 0x3, sft), ioc_base[0] + 0x38); } static void gpio0_set_direct(u32 pin_id, int out) { u32 sft = (pin_id % 16); if (pin_id < 16) writel_relaxed(BITS_WITH_WMASK(out, 0x1, sft), gpio_base[0] + RV1106_GPIO_SWPORT_DDR_L); else writel_relaxed(BITS_WITH_WMASK(out, 0x1, sft), gpio_base[0] + RV1106_GPIO_SWPORT_DDR_H); } static void gpio0_set_lvl(u32 pin_id, int lvl) { u32 sft = (pin_id % 16); if (pin_id < 16) writel_relaxed(BITS_WITH_WMASK(lvl, 0x1, sft), gpio_base[0] + RV1106_GPIO_SWPORT_DR_L); else writel_relaxed(BITS_WITH_WMASK(lvl, 0x1, sft), gpio_base[0] + RV1106_GPIO_SWPORT_DR_H); } static void gpio_config(void) { u32 iomux, dir, lvl, pull, id; u32 cfg, i; if (slp_cfg == NULL) return; ddr_data.gpio0a_iomux_l = readl_relaxed(ioc_base[0] + 0); ddr_data.gpio0a_iomux_h = readl_relaxed(ioc_base[0] + 0x4); ddr_data.gpio0a0_pull = readl_relaxed(ioc_base[0] + 0x38); ddr_data.gpio0_ddr_l = readl_relaxed(gpio_base[0] + RV1106_GPIO_SWPORT_DDR_L); ddr_data.gpio0_ddr_h = readl_relaxed(gpio_base[0] + RV1106_GPIO_SWPORT_DDR_H); for (i = 0; i < slp_cfg->sleep_io_config_cnt; i++) { cfg = slp_cfg->sleep_io_config[i]; iomux = RKPM_IO_CFG_GET_IOMUX(cfg); dir = RKPM_IO_CFG_GET_GPIO_DIR(cfg); lvl = RKPM_IO_CFG_GET_GPIO_LVL(cfg); pull = RKPM_IO_CFG_GET_PULL(cfg); id = RKPM_IO_CFG_GET_ID(cfg); if (is_rv1106 && id == 3) { /* gpio0_a3, pullnone */ gpio0_set_iomux(3, 1); gpio0_set_pull(3, RV1106_GPIO_PULL_NONE); continue; } if (is_rv1103 && id == 4) { /* gpio0_a4, pullnone */ gpio0_set_iomux(4, 1); gpio0_set_pull(4, RV1106_GPIO_PULL_NONE); continue; } if (iomux == RKPM_IO_CFG_IOMUX_GPIO_VAL) { if (dir == RKPM_IO_CFG_GPIO_DIR_INPUT_VAL) gpio0_set_lvl(id, lvl); gpio0_set_direct(id, dir); } gpio0_set_iomux(id, iomux); gpio0_set_pull(id, pull); } } static void gpio_restore(void) { if (slp_cfg == NULL) return; writel_relaxed(WITH_16BITS_WMSK(ddr_data.gpio0a_iomux_l), ioc_base[0] + 0); writel_relaxed(WITH_16BITS_WMSK(ddr_data.gpio0a_iomux_h), ioc_base[0] + 0x4); writel_relaxed(WITH_16BITS_WMSK(ddr_data.gpio0a0_pull), ioc_base[0] + 0x38); writel_relaxed(WITH_16BITS_WMSK(ddr_data.gpio0_ddr_l), gpio_base[0] + RV1106_GPIO_SWPORT_DDR_L); writel_relaxed(WITH_16BITS_WMSK(ddr_data.gpio0_ddr_h), gpio_base[0] + RV1106_GPIO_SWPORT_DDR_H); } static struct uart_debug_ctx debug_port_save; static u32 cru_mode; static u32 pvtpll0_length, pvtpll1_length; static void pvtpllcru_save(void) { pvtpll0_length = readl_relaxed(pvtpllcru_base + CRU_PVTPLL0_CON0_H); pvtpll1_length = readl_relaxed(pvtpllcru_base + CRU_PVTPLL1_CON0_H); } static void pvtpllcru_restore(void) { writel_relaxed(0x00030000, pvtpllcru_base + CRU_PVTPLL0_CON0_L); writel_relaxed(0x007f0000 | pvtpll0_length, pvtpllcru_base + CRU_PVTPLL0_CON0_H); writel_relaxed(0xffff0018, pvtpllcru_base + CRU_PVTPLL0_CON1_L); writel_relaxed(0xffff0004, pvtpllcru_base + CRU_PVTPLL0_CON2_H); writel_relaxed(0x00030003, pvtpllcru_base + CRU_PVTPLL0_CON0_L); writel_relaxed(0x00030000, pvtpllcru_base + CRU_PVTPLL1_CON0_L); writel_relaxed(0x007f0000 | pvtpll1_length, pvtpllcru_base + CRU_PVTPLL1_CON0_H); writel_relaxed(0xffff0018, pvtpllcru_base + CRU_PVTPLL1_CON1_L); writel_relaxed(0xffff0004, pvtpllcru_base + CRU_PVTPLL1_CON2_H); writel_relaxed(0x00030003, pvtpllcru_base + CRU_PVTPLL1_CON0_L); } static void vd_log_regs_save(void) { cru_mode = readl_relaxed(cru_base + 0x280); rkpm_printch('a'); gic400_save(); rkpm_printch('b'); pvtpllcru_save(); rkpm_reg_rgn_save(vd_core_reg_rgns, ARRAY_SIZE(vd_core_reg_rgns)); rkpm_printch('c'); rkpm_reg_rgn_save(vd_log_reg_rgns, ARRAY_SIZE(vd_log_reg_rgns)); rkpm_printch('d'); rkpm_uart_debug_save(uartdbg_base, &debug_port_save); rkpm_printch('e'); } static void vd_log_regs_restore(void) { rkpm_uart_debug_restore(uartdbg_base, &debug_port_save); /* slow mode */ writel_relaxed(0x003f0000, cru_base + 0x280); rkpm_reg_rgn_restore(vd_core_reg_rgns, ARRAY_SIZE(vd_core_reg_rgns)); rkpm_reg_rgn_restore(vd_log_reg_rgns, ARRAY_SIZE(vd_log_reg_rgns)); pvtpllcru_restore(); /* wait lock */ pm_pll_wait_lock(RV1106_APLL_ID); pm_pll_wait_lock(RV1106_CPLL_ID); pm_pll_wait_lock(RV1106_GPLL_ID); /* restore mode */ writel_relaxed(WITH_16BITS_WMSK(cru_mode), cru_base + 0x280); gic400_restore(); writel_relaxed(0xffff0000, pmugrf_base + RV1106_PMUGRF_SOC_CON(4)); writel_relaxed(0xffff0000, pmugrf_base + RV1106_PMUGRF_SOC_CON(5)); if (readl_relaxed(wdt_ns_base + RV1106_WDT_CR) & 0x1) writel_relaxed(0x76, wdt_ns_base + RV1106_WDT_CRR); if (readl_relaxed(wdt_s_base + RV1106_WDT_CR) & 0x1) writel_relaxed(0x76, wdt_s_base + RV1106_WDT_CRR); } static void rkpm_reg_rgns_init(void) { rkpm_alloc_region_mem(vd_core_reg_rgns, ARRAY_SIZE(vd_core_reg_rgns)); rkpm_alloc_region_mem(vd_log_reg_rgns, ARRAY_SIZE(vd_log_reg_rgns)); } static void rkpm_regs_rgn_dump(void) { return; rkpm_dump_reg_rgns(vd_core_reg_rgns, ARRAY_SIZE(vd_core_reg_rgns)); rkpm_dump_reg_rgns(vd_log_reg_rgns, ARRAY_SIZE(vd_log_reg_rgns)); } static int rockchip_lpmode_enter(unsigned long arg) { flush_cache_all(); cpu_do_idle(); #if RV1106_WAKEUP_TO_SYSTEM_RESET /* If reaches here, it means wakeup source cames before cpu enter wfi. * So we should do system reset if RV1106_WAKEUP_TO_SYSTEM_RESET. */ writel_relaxed(0x000c000c, cru_base + RV1106_CRU_GLB_RST_CON); writel_relaxed(0xffff0000, pmugrf_base + RV1106_PMUGRF_SOC_CON(4)); writel_relaxed(0xffff0000, pmugrf_base + RV1106_PMUGRF_SOC_CON(5)); dsb(sy); writel_relaxed(0xfdb9, cru_base + RV1106_CRU_GLB_SRST_FST); #endif rkpm_printstr("Failed to suspend\n"); return 1; } static int rv1106_suspend_enter(suspend_state_t state) { rkpm_printstr("rv1106 enter sleep\n"); slp_cfg = rockchip_get_cur_sleep_config(); local_fiq_disable(); rv1106_dbg_irq_prepare(); rkpm_printch('-'); RE_ENTER_SLEEP: clock_suspend(); rkpm_printch('0'); soc_sleep_config(); rkpm_printch('1'); plls_suspend(); rkpm_printch('2'); gpio_config(); rkpm_printch('3'); vd_log_regs_save(); rkpm_regs_rgn_dump(); rkpm_printch('4'); rkpm_printstr("-WFI-"); cpu_suspend(0, rockchip_lpmode_enter); rkpm_printch('4'); vd_log_regs_restore(); rkpm_printch('3'); rkpm_regs_rgn_dump(); gpio_restore(); rkpm_printch('2'); plls_resume(); rkpm_printch('1'); soc_sleep_restore(); rkpm_printch('0'); clock_resume(); rkpm_printch('-'); /* Check whether it's time_out wakeup */ if (IS_ENABLED(CONFIG_RV1106_HPMCU_FAST_WAKEUP)) { if (hpmcu_fast_wkup()) { rkpm_gicv2_dist_restore(gicd_base, &gicd_ctx_save); goto RE_ENTER_SLEEP; } else { rkpm_gicv2_dist_restore(gicd_base, &gicd_ctx_save); rkpm_gicv2_cpu_restore(gicd_base, gicc_base, &gicc_ctx_save); } } if (rk_hptimer_get_mode(hptimer_base) == RK_HPTIMER_SOFT_ADJUST_MODE) { if (rk_hptimer_wait_mode(hptimer_base, RK_HPTIMER_SOFT_ADJUST_MODE)) rkpm_printstr("WARN: can't wait hptimer soft adjust mode!\n"); } fiq_glue_resume(); rv1106_dbg_irq_finish(); local_fiq_enable(); rkpm_printstr("rv1106 exit sleep\n"); return 0; } static int __init rv1106_suspend_init(struct device_node *np) { void __iomem *dev_reg_base; if (of_machine_is_compatible("rockchip,rv1103")) is_rv1103 = 1; else if (of_machine_is_compatible("rockchip,rv1106")) is_rv1106 = 1; dev_reg_base = ioremap(RV1106_DEV_REG_BASE, RV1106_DEV_REG_SIZE); if (dev_reg_base) pr_info("%s map dev_reg 0x%x -> 0x%x\n", __func__, RV1106_DEV_REG_BASE, (u32)dev_reg_base); else pr_err("%s: can't map dev_reg(0x%x)\n", __func__, RV1106_DEV_REG_BASE); gicd_base = dev_reg_base + RV1106_GIC_OFFSET + 0x1000; gicc_base = dev_reg_base + RV1106_GIC_OFFSET + 0x2000; hptimer_base = dev_reg_base + RV1106_HPTIMER_OFFSET; firewall_ddr_base = dev_reg_base + RV1106_FW_DDR_OFFSET; firewall_syssram_base = dev_reg_base + RV1106_FW_SRAM_OFFSET; nstimer_base = dev_reg_base + RV1106_NSTIMER_OFFSET; stimer_base = dev_reg_base + RV1106_STIMER_OFFSET; wdt_ns_base = dev_reg_base + RV1106_WDTNS_OFFSET; wdt_s_base = dev_reg_base + RV1106_WDTS_OFFSET; pmu_base = dev_reg_base + RV1106_PMU_OFFSET; uartdbg_base = dev_reg_base + RV1106_UART2_OFFSET; pmupvtm_base = dev_reg_base + RV1106_PMUPVTM_OFFSET; pmusgrf_base = dev_reg_base + RV1106_PMUSGRF_OFFSET; ddrc_base = dev_reg_base + RV1106_DDRC_OFFSET; perigrf_base = dev_reg_base + RV1106_PERIGRF_OFFSET; vencgrf_base = dev_reg_base + RV1106_VENCGRF_OFFSET; npugrf_base = dev_reg_base + RV1106_NPUGRF_OFFSET; pmugrf_base = dev_reg_base + RV1106_PMUGRF_OFFSET; ddrgrf_base = dev_reg_base + RV1106_DDRGRF_OFFSET; coregrf_base = dev_reg_base + RV1106_COREGRF_OFFSET; vigrf_base = dev_reg_base + RV1106_VIGRF_OFFSET; vogrf_base = dev_reg_base + RV1106_VOGRF_OFFSET; perisgrf_base = dev_reg_base + RV1106_PERISGRF_OFFSET; visgrf_base = dev_reg_base + RV1106_VIGRF_OFFSET; npusgrf_base = dev_reg_base + RV1106_NPUSGRF_OFFSET; coresgrf_base = dev_reg_base + RV1106_CORESGRF_OFFSET; vencsgrf_base = dev_reg_base + RV1106_VENCSGRF_OFFSET; vosgrf_base = dev_reg_base + RV1106_VOSGRF_OFFSET; pmusgrf_base = dev_reg_base + RV1106_PMUSGRF_OFFSET; pmucru_base = dev_reg_base + RV1106_PMUCRU_OFFSET; cru_base = dev_reg_base + RV1106_CRU_OFFSET; pvtpllcru_base = dev_reg_base + RV1106_PVTPLLCRU_OFFSET; pericru_base = dev_reg_base + RV1106_PERICRU_OFFSET; vicru_base = dev_reg_base + RV1106_VICRU_OFFSET; npucru_base = dev_reg_base + RV1106_NPUCRU_OFFSET; corecru_base = dev_reg_base + RV1106_CORECRU_OFFSET; venccru_base = dev_reg_base + RV1106_VENCCRU_OFFSET; vocru_base = dev_reg_base + RV1106_VOCRU_OFFSET; mbox_base = dev_reg_base + RV1106_MBOX_OFFSET; ioc_base[0] = dev_reg_base + RV1106_GPIO0IOC_OFFSET; ioc_base[1] = dev_reg_base + RV1106_GPIO1IOC_OFFSET; ioc_base[2] = dev_reg_base + RV1106_GPIO2IOC_OFFSET; ioc_base[3] = dev_reg_base + RV1106_GPIO3IOC_OFFSET; ioc_base[4] = dev_reg_base + RV1106_GPIO4IOC_OFFSET; gpio_base[0] = dev_reg_base + RV1106_GPIO0_OFFSET; gpio_base[1] = dev_reg_base + RV1106_GPIO1_OFFSET; gpio_base[2] = dev_reg_base + RV1106_GPIO2_OFFSET; gpio_base[3] = dev_reg_base + RV1106_GPIO3_OFFSET; gpio_base[4] = dev_reg_base + RV1106_GPIO4_OFFSET; rv1106_bootram_base = dev_reg_base + RV1106_PMUSRAM_OFFSET; rv1106_config_bootdata(); /* copy resume code and data to bootsram */ memcpy(rv1106_bootram_base, rockchip_slp_cpu_resume, rv1106_bootram_sz + 0x50); /* remap */ #if RV1106_WAKEUP_TO_SYSTEM_RESET writel_relaxed(BITS_WITH_WMASK(1, 0x1, 10), pmusgrf_base + RV1106_PMUSGRF_SOC_CON(1)); #else writel_relaxed(BITS_WITH_WMASK(0, 0x1, 10), pmusgrf_base + RV1106_PMUSGRF_SOC_CON(1)); #endif /* biu auto con */ writel_relaxed(0x07ff07ff, pmu_base + RV1106_PMU_BIU_AUTO_CON); /* gpio0_a3 activelow, gpio0_a4 active high, select sleep func */ writel_relaxed(BITS_WITH_WMASK(0x5, 0x7, 0), pmugrf_base + RV1106_PMUGRF_SOC_CON(1)); rkpm_region_mem_init(RV1106_PM_REG_REGION_MEM_SIZE); rkpm_reg_rgns_init(); return 0; } static const struct platform_suspend_ops rv1106_suspend_ops = { .enter = rv1106_suspend_enter, .valid = suspend_valid_only_mem, }; static const struct rockchip_pm_data rv1106_pm_data __initconst = { .ops = &rv1106_suspend_ops, .init = rv1106_suspend_init, }; /****************************************************************************/ static const struct of_device_id rockchip_pmu_of_device_ids[] __initconst = { { .compatible = "rockchip,rv1106-pmu", .data = &rv1106_pm_data, }, { /* sentinel */ }, }; void __init rockchip_suspend_init(void) { const struct rockchip_pm_data *pm_data; const struct of_device_id *match; struct device_node *np; int ret; np = of_find_matching_node_and_match(NULL, rockchip_pmu_of_device_ids, &match); if (!match) { pr_err("Failed to find PMU node\n"); return; } pm_data = (struct rockchip_pm_data *)match->data; if (pm_data->init) { ret = pm_data->init(np); if (ret) { pr_err("%s: matches init error %d\n", __func__, ret); return; } } suspend_set_ops(pm_data->ops); }