From 3e7c4ab3ff451ef740a9bb23cf79e7a55eab8273 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Mon, 18 Aug 2014 21:37:06 +0800 Subject: [PATCH] rk312x: support suspend and resume MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: 许盛飞 --- arch/arm/boot/dts/rk312x-clocks.dtsi | 20 +- arch/arm/boot/dts/rk312x.dtsi | 13 + arch/arm/mach-rockchip/cpu_axi.h | 26 + arch/arm/mach-rockchip/pm-rk312x.c | 916 +++++++++++++++++++++++++++ arch/arm/mach-rockchip/rk312x.c | 204 +++++- include/linux/rockchip/cru.h | 27 +- include/linux/rockchip/pmu.h | 23 + 7 files changed, 1203 insertions(+), 26 deletions(-) create mode 100644 arch/arm/mach-rockchip/pm-rk312x.c diff --git a/arch/arm/boot/dts/rk312x-clocks.dtsi b/arch/arm/boot/dts/rk312x-clocks.dtsi index 97f15d4f0ccf..78e7a56f2d12 100755 --- a/arch/arm/boot/dts/rk312x-clocks.dtsi +++ b/arch/arm/boot/dts/rk312x-clocks.dtsi @@ -1552,7 +1552,7 @@ "clk_crypto", "clk_i2s_2ch_out", "clk_i2s_2ch", "clk_testout"; - rockchip,suspend-clkgating-setting=<0x0 0x0>; + rockchip,suspend-clkgating-setting=<0x11ff 0x0>; #clock-cells = <1>; }; @@ -1586,7 +1586,7 @@ "clk_uart2_div", "uart2_frac", "clk_tsp", "reserved"; - rockchip,suspend-clkgating-setting=<0x0 0x0>; + rockchip,suspend-clkgating-setting=<0x000f 0x0>; #clock-cells = <1>; }; @@ -1617,7 +1617,7 @@ "spdif_frac", "clk_sdio", "clk_emmc", "clk_mipi_24m"; - rockchip,suspend-clkgating-setting=<0x0 0x0>; + rockchip,suspend-clkgating-setting=<0x000f 0x0>; #clock-cells = <1>; }; @@ -1650,7 +1650,7 @@ "hclk_vdpu", "clk_gpu", "g_hclk_gps", "clk_sfc"; - rockchip,suspend-clkgating-setting=<0x0000 0x0000>; + rockchip,suspend-clkgating-setting=<0x0060 0x0000>; #clock-cells = <1>; }; @@ -1685,7 +1685,7 @@ "g_aclk_intmem", "reserved", "reserved", "reserved"; - rockchip,suspend-clkgating-setting = <0x0000 0x0000>; + rockchip,suspend-clkgating-setting = <0xff8f 0x0000>; #clock-cells = <1>; }; @@ -1718,7 +1718,7 @@ "reserved", "g_hclk_otg0", "g_pclk_acodec", "reserved"; - rockchip,suspend-clkgating-setting = <0x0000 0x0000>; + rockchip,suspend-clkgating-setting = <0x00f0 0x0000>; #clock-cells = <1>; }; @@ -1786,7 +1786,7 @@ "g_pclk_spi0", "reserved", "g_pclk_saradc", "g_pclk_wdt"; - rockchip,suspend-clkgating-setting = <0x0000 0x0000>; + rockchip,suspend-clkgating-setting = <0x8080 0x0000>; #clock-cells = <1>; }; @@ -1820,7 +1820,7 @@ "g_pclk_gpio3", "reserved", "reserved", "reserved"; - rockchip,suspend-clkgating-setting=<0x0000 0x0000>; + rockchip,suspend-clkgating-setting=<0xff0f 0x0000>; #clock-cells = <1>; }; @@ -1853,7 +1853,7 @@ "g_pclk_sim_card", "g_hclk_usb_peri", "g_hclk_pe_arbi", "g_aclk_peri_niu"; - rockchip,suspend-clkgating-setting=<0x0 0x0>; + rockchip,suspend-clkgating-setting=<0xf00f 0x0>; #clock-cells = <1>; }; @@ -1887,7 +1887,7 @@ "g_hclk_tsp", "g_clkin0_tsp", "g_hclk_usbhost", "clk_nandc"; - rockchip,suspend-clkgating-setting = <0x0 0x0>; /* pwm logic vol */ + rockchip,suspend-clkgating-setting = <0x0000 0x0>; /* pwm logic vol */ #clock-cells = <1>; }; diff --git a/arch/arm/boot/dts/rk312x.dtsi b/arch/arm/boot/dts/rk312x.dtsi index 43372be7b68e..016bbe305147 100755 --- a/arch/arm/boot/dts/rk312x.dtsi +++ b/arch/arm/boot/dts/rk312x.dtsi @@ -945,4 +945,17 @@ }; }; }; + rockchip_suspend { + rockchip,ctrbits = < + (0 + |RKPM_CTR_PWR_DMNS + |RKPM_CTR_GTCLKS + |RKPM_CTR_PLLS + |RKPM_CTR_ARMOFF_LPMD + ) + >; + rockchip,pmic-suspend_gpios = < + GPIO1_A1 + >; + }; }; diff --git a/arch/arm/mach-rockchip/cpu_axi.h b/arch/arm/mach-rockchip/cpu_axi.h index 53a94f108285..9af5a7ac359c 100644 --- a/arch/arm/mach-rockchip/cpu_axi.h +++ b/arch/arm/mach-rockchip/cpu_axi.h @@ -99,4 +99,30 @@ #define RK3288_CPU_AXI_HEVC_R_QOS_VIRT (RK3288_SERVICE_HEVC_VIRT + 0x0) #define RK3288_CPU_AXI_HEVC_W_QOS_VIRT (RK3288_SERVICE_HEVC_VIRT + 0x100) +#define RK312X_CPU_AXI_QOS_NUM_REGS 4 +#define RK312X_CPU_AXI_SAVE_QOS(array, base) do { \ + array[0] = readl_relaxed(base + CPU_AXI_QOS_PRIORITY); \ + array[1] = readl_relaxed(base + CPU_AXI_QOS_MODE); \ + array[2] = readl_relaxed(base + CPU_AXI_QOS_BANDWIDTH); \ + array[3] = readl_relaxed(base + CPU_AXI_QOS_SATURATION); \ +} while (0) +#define RK312X_CPU_AXI_RESTORE_QOS(array, base) do { \ + writel_relaxed(array[0], base + CPU_AXI_QOS_PRIORITY); \ + writel_relaxed(array[1], base + CPU_AXI_QOS_MODE); \ + writel_relaxed(array[2], base + CPU_AXI_QOS_BANDWIDTH); \ + writel_relaxed(array[3], base + CPU_AXI_QOS_SATURATION); \ +} while (0) +#define RK312X_SERVICE_VIO_VIRT (RK_CPU_AXI_BUS_VIRT + 0x7000) + +#define RK312X_CPU_AXI_VIO_RGA_QOS_VIRT (RK312X_SERVICE_VIO_VIRT) +#define RK312X_CPU_AXI_VIO_EBC_QOS_VIRT (RK312X_SERVICE_VIO_VIRT + 0x80) +#define RK312X_CPU_AXI_VIO_IEP_QOS_VIRT (RK312X_SERVICE_VIO_VIRT + 0x100) +#define RK312X_CPU_AXI_VIO_LCDC0_QOS_VIRT (RK312X_SERVICE_VIO_VIRT + 0x180) +#define RK312X_CPU_AXI_VIO_VIP0_QOS_VIRT (RK312X_SERVICE_VIO_VIRT + 0x200) + +#define RK312X_SERVICE_GPU_VIRT (RK_CPU_AXI_BUS_VIRT + 0x5000) +#define RK312X_CPU_AXI_GPU_QOS_VIRT (RK312X_SERVICE_GPU_VIRT) + +#define RK312X_SERVICE_VIDEO_VIRT (RK_CPU_AXI_BUS_VIRT + 0x6000) +#define RK312X_CPU_AXI_VIDEO_QOS_VIRT (RK312X_SERVICE_VIDEO_VIRT) #endif diff --git a/arch/arm/mach-rockchip/pm-rk312x.c b/arch/arm/mach-rockchip/pm-rk312x.c new file mode 100644 index 000000000000..18f3e125b05d --- /dev/null +++ b/arch/arm/mach-rockchip/pm-rk312x.c @@ -0,0 +1,916 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pm.h" +#include + +__weak void rk_usb_power_down(void); +__weak void rk_usb_power_up(void); + +#define GPIO_INTEN 0x30 +#define GPIO_INT_STATUS 0x40 +#define GIC_DIST_PENDING_SET 0x200 +#define DUMP_GPIO_INTEN(ID)\ +do { \ + u32 en = readl_relaxed(RK_GPIO_VIRT(ID) + GPIO_INTEN);\ + if (en) {\ + rkpm_ddr_printascii("GPIO" #ID "_INTEN: "); \ + rkpm_ddr_printhex(en); \ + rkpm_ddr_printch('\n'); \ + } \ +} while (0) +#define RK312X_CRU_UNGATING_OPS(id) cru_writel(\ + CRU_W_MSK_SETBITS(0, (id) % 16, 0x1), RK312X_CRU_GATEID_CONS((id))) +#define RK312X_CRU_GATING_OPS(id) cru_writel(\ + CRU_W_MSK_SETBITS(1, (id) % 16, 0x1), RK312X_CRU_GATEID_CONS((id))) + +enum rk_plls_id { + APLL_ID = 0, + DPLL_ID, + CPLL_ID, + GPLL_ID, + END_PLL_ID, +}; + +static inline void uart_printch(char bbyte) +{ + u32 reg_save[2]; + u32 u_clk_id = (RK312X_CLKGATE_UART0_SRC + CONFIG_RK_DEBUG_UART * 2); + u32 u_pclk_id = (RK312X_CLKGATE_PCLK_UART0 + CONFIG_RK_DEBUG_UART); + + reg_save[0] = cru_readl(RK312X_CRU_GATEID_CONS(u_clk_id)); + reg_save[1] = cru_readl(RK312X_CRU_GATEID_CONS(u_pclk_id)); + RK312X_CRU_UNGATING_OPS(u_clk_id); + RK312X_CRU_UNGATING_OPS(u_pclk_id); + rkpm_udelay(1); + +write_uart: + writel_relaxed(bbyte, RK_DEBUG_UART_VIRT); + dsb(); + + while (!(readl_relaxed(RK_DEBUG_UART_VIRT + 0x14) & 0x40)) + barrier(); + + if (bbyte == '\n') { + bbyte = '\r'; + goto write_uart; + } + + cru_writel(reg_save[0] | CRU_W_MSK(u_clk_id + % 16, 0x1), RK312X_CRU_GATEID_CONS(u_clk_id)); + cru_writel(reg_save[1] | CRU_W_MSK(u_pclk_id + % 16, 0x1), RK312X_CRU_GATEID_CONS(u_pclk_id)); + + if (0) { +write_uart1: + writel_relaxed(bbyte, RK_DEBUG_UART_VIRT); + dsb(); + + while (!(readl_relaxed(RK_DEBUG_UART_VIRT + 0x14) & 0x40)) + barrier(); + if (bbyte == '\n') { + bbyte = '\r'; + goto write_uart1; + } + } +} + +void PIE_FUNC(sram_printch)(char bbyte) +{ + /*uart_printch(byte);*/ +} +static void pll_udelay(u32 udelay) +{ + u32 mode; + + mode = cru_readl(RK312X_CRU_MODE_CON); + cru_writel(RK312X_PLL_MODE_SLOW(APLL_ID), RK312X_CRU_MODE_CON); + rkpm_udelay(udelay * 5); + cru_writel(mode|(RK312X_PLL_MODE_MSK(APLL_ID) + << 16), RK312X_CRU_MODE_CON); +} + +#define RK312X_PLL_PWR_DN_MSK (0x01 << 1) +#define RK312X_PLL_BYPASS CRU_W_MSK_SETBITS(1, 0xF, 0x01) +#define RK312X_PLL_NOBYPASS CRU_W_MSK_SETBITS(0, 0xF, 0x01) +#define RK312X_PLL_RESET CRU_W_MSK_SETBITS(1, 14, 0x01) +#define RK312X_PLL_RESET_RESUME CRU_W_MSK_SETBITS(0, 14, 0x01) + +static u32 plls_con0_save[END_PLL_ID]; +static u32 plls_con1_save[END_PLL_ID]; +static u32 plls_con2_save[END_PLL_ID]; +static u32 cru_mode_con; + +static void pm_pll_wait_lock(u32 pll_idx) +{ + u32 delay = 600000U; + + dsb(); + dsb(); + dsb(); + dsb(); + dsb(); + dsb(); + while (delay > 0) { + if ((cru_readl(RK312X_PLL_CONS(pll_idx, 1)) & (0x1 << 10))) + break; + delay--; + } + if (delay == 0) { + rkpm_ddr_printascii("unlock-pll:"); + rkpm_ddr_printhex(pll_idx); + rkpm_ddr_printch('\n'); + } +} + +static inline void plls_suspend(u32 pll_id) +{ + plls_con0_save[pll_id] = cru_readl(RK312X_PLL_CONS((pll_id), 0)); + plls_con1_save[pll_id] = cru_readl(RK312X_PLL_CONS((pll_id), 1)); + plls_con2_save[pll_id] = cru_readl(RK312X_PLL_CONS((pll_id), 2)); + + cru_writel(RK312X_PLL_BYPASS, RK312X_PLL_CONS((pll_id), 0)); +} +static inline void plls_resume(u32 pll_id) +{ + u32 pllcon0, pllcon1, pllcon2; + + cru_writel(RK312X_PLL_MODE_SLOW(pll_id), RK312X_CRU_MODE_CON); + /*cru_writel(RK312X_PLL_NOBYPASS, RK312x_PLL_CONS((pll_id), 0));*/ + + pllcon0 = plls_con0_save[pll_id]; + /*cru_readl(RK312x_PLL_CONS((pll_id),0));*/ + pllcon1 = plls_con1_save[pll_id]; + /*cru_readl(RK12x_PLL_CONS((pll_id),1));*/ + pllcon2 = plls_con2_save[pll_id]; + /*cru_readl(RK312x_PLL_CONS((pll_id),2));*/ + + /*cru_writel(RK312X_PLL_RESET, RK312X_PLL_CONS(pll_id, 1));*/ + + cru_writel(pllcon2, RK312X_PLL_CONS(pll_id, 2)); + cru_writel(pllcon1 | 0xf5ff0000, RK312X_PLL_CONS(pll_id, 1)); + cru_writel(pllcon0 | 0xffff0000, RK312X_PLL_CONS(pll_id, 0)); + + pll_udelay(5); + + /*return form rest*/ + /*cru_writel(RK312X_PLL_RESET_RESUME, RK312X_PLL_CONS(pll_id, 1));*/ + + /*wating lock state*/ + pll_udelay(168); + pm_pll_wait_lock(pll_id); +} +static u32 clk_sel0, clk_sel1, clk_sel10, clk_sel24, clk_sel29; +static void pm_plls_suspend(void) +{ + clk_sel0 = cru_readl(RK312X_CRU_CLKSELS_CON(0)); + clk_sel1 = cru_readl(RK312X_CRU_CLKSELS_CON(1)); + clk_sel10 = cru_readl(RK312X_CRU_CLKSELS_CON(10)); + clk_sel24 = cru_readl(RK312X_CRU_CLKSELS_CON(24)); + clk_sel29 = cru_readl(RK312X_CRU_CLKSELS_CON(29)); + + cru_mode_con = cru_readl(RK312X_CRU_MODE_CON); + + /*CPLL*/ + cru_writel(RK312X_PLL_MODE_SLOW(CPLL_ID), RK312X_CRU_MODE_CON); + + /*GPLL*/ + cru_writel(RK312X_PLL_MODE_SLOW(GPLL_ID), RK312X_CRU_MODE_CON); + + /*crypto*/ + cru_writel(CRU_W_MSK_SETBITS(3, 0, 0x3), RK312X_CRU_CLKSELS_CON(24)); + + /* peri aclk hclk pclk*/ + cru_writel(0 + |CRU_W_MSK_SETBITS(0, 0, 0x1f)/*1 aclk*/ + |CRU_W_MSK_SETBITS(0, 8, 0x3)/*2 hclk 0 1:1,1 2:1 ,2 4:1*/ + |CRU_W_MSK_SETBITS(0, 12, 0x3)/* 2 0~3 1 2 4 8 div*/ + , RK312X_CRU_CLKSELS_CON(10)); + + /* pmu*/ + cru_writel(CRU_W_MSK_SETBITS(0, 8, 0x1f), RK312X_CRU_CLKSELS_CON(29)); + plls_suspend(CPLL_ID); + plls_suspend(GPLL_ID); + + /*apll*/ + cru_writel(RK312X_PLL_MODE_SLOW(APLL_ID), RK312X_CRU_MODE_CON); + /*a7_core*/ + cru_writel(0 | CRU_W_MSK_SETBITS(0, 0, 0x1f) + , RK312X_CRU_CLKSELS_CON(0)); + + /*pclk_dbg*/ + cru_writel(0 | CRU_W_MSK_SETBITS(3, 0, 0x7) + , RK312X_CRU_CLKSELS_CON(1)); + plls_suspend(APLL_ID); +} + +static void pm_plls_resume(void) +{ + cru_writel(clk_sel0 | CRU_W_MSK(0, 0x1f), RK312X_CRU_CLKSELS_CON(0)); + cru_writel(clk_sel1 | CRU_W_MSK(0, 0x7), RK312X_CRU_CLKSELS_CON(1)); + + plls_resume(APLL_ID); + cru_writel(cru_mode_con + |(RK312X_PLL_MODE_MSK(APLL_ID) << 16), RK312X_CRU_MODE_CON); + + + /*peri aclk hclk pclk*/ + cru_writel(clk_sel10 | (CRU_W_MSK(0, 0x1f) | CRU_W_MSK(8, 0x3) + | CRU_W_MSK(12, 0x3)), RK312X_CRU_CLKSELS_CON(10)); + + /* crypto*/ + cru_writel(clk_sel24 | CRU_W_MSK(0, 0x3), RK312X_CRU_CLKSELS_CON(24)); + + cru_writel(clk_sel29 | CRU_W_MSK(8, 0x1f), RK312X_CRU_CLKSELS_CON(29)); + + /* pmu alive */ + plls_resume(GPLL_ID); + cru_writel(cru_mode_con | (RK312X_PLL_MODE_MSK(GPLL_ID) << 16) + , RK312X_CRU_MODE_CON); + + plls_resume(CPLL_ID); + cru_writel(cru_mode_con | (RK312X_PLL_MODE_MSK(CPLL_ID) << 16) + , RK312X_CRU_MODE_CON); +} +#ifdef CONFIG_RK_LAST_LOG +extern void rk_last_log_text(char *text, size_t size); +#endif + +static void ddr_printch(char byte) +{ + uart_printch(byte); +#ifdef CONFIG_RK_LAST_LOG + rk_last_log_text(&byte, 1); + + if (byte == '\n') { + byte = '\r'; + rk_last_log_text(&byte, 1); + } +#endif + pll_udelay(2); +} + +static noinline void rk312x_pm_dump_inten(void) +{ + DUMP_GPIO_INTEN(0); + DUMP_GPIO_INTEN(1); + DUMP_GPIO_INTEN(2); + DUMP_GPIO_INTEN(3); +} +static noinline void rk312x_pm_dump_irq(void) +{ + u32 irq[4]; + int i; + + u32 irq_gpio = (readl_relaxed(RK_GIC_VIRT + +GIC_DIST_PENDING_SET + 8) >> 4) & 0x0F; + + for (i = 0; i < ARRAY_SIZE(irq); i++) + irq[i] = readl_relaxed(RK_GIC_VIRT + +GIC_DIST_PENDING_SET + (1 + i) * 4); + for (i = 0; i < ARRAY_SIZE(irq); i++) { + if (irq[i]) + log_wakeup_reason(32 * (i + 1) + fls(irq[i]) - 1); + } + for (i = 0; i <= 3; i++) { + if (irq_gpio & (1 << i)) { + pr_debug("wakeup gpio%d: %08x\n", i + , readl_relaxed(RK_GPIO_VIRT(i) + + GPIO_INT_STATUS)); + rkpm_ddr_printascii("wakeup gpio"); + rkpm_ddr_printhex(i); + rkpm_ddr_printascii(":"); + rkpm_ddr_printhex(readl_relaxed(RK_GPIO_VIRT(i) + + GPIO_INT_STATUS)); + rkpm_ddr_printch('\n'); + } + } +} + +static void rkpm_prepare(void) +{ + rk312x_pm_dump_inten(); +} +static void rkpm_finish(void) +{ + rk312x_pm_dump_irq(); +} +enum rk312x_pwr_mode_con { + pmu_power_mode_en = 0, + pmu_clk_core_src_gate_en, + pmu_clk_bus_src_gate_en, + pmu_global_int_disable, + + pmu_core_pd_en, + pmu_use_if, + pmu_wait_osc_24m, + pmu_sref_enter_en, + + pmu_ddr_gating_en, + pmu_int_en, + pmu_ddr0io_ret_de_req, + + pmu_clr_crypto = 16, + pmu_clr_msch, + pmu_clr_sys, + pmu_clr_core, + + pmu_clr_gpu, + pmu_clr_vio, + pmu_clr_video, + pmu_clr_peri, +}; +#define SOC_REMAP 12 +#define GRF_SOC_CON0 0x140 + +#define RK312X_IMEM_VIRT (RK_BOOTRAM_VIRT + SZ_32K) +#define RKPM_BOOTRAM_PHYS (RK312X_IMEM_PHYS) +#define RKPM_BOOTRAM_BASE (RK312X_IMEM_VIRT) +#define RKPM_BOOTRAM_SIZE (RK312X_IMEM_SIZE) + +/*sys resume code in boot ram*/ +#define RKPM_BOOT_CODE_PHY (RKPM_BOOTRAM_PHYS + RKPM_BOOT_CODE_OFFSET) +#define RKPM_BOOT_CODE_BASE (RKPM_BOOTRAM_BASE + RKPM_BOOT_CODE_OFFSET) + + +/*sys resume data in boot ram*/ +#define RKPM_BOOT_DATA_PHY (RKPM_BOOTRAM_PHYS + RKPM_BOOT_DATA_OFFSET) +#define RKPM_BOOT_DATA_BASE (RKPM_BOOTRAM_BASE + RKPM_BOOT_DATA_OFFSET) + +/*ddr resume data in boot ram*/ +#define RKPM_BOOT_DDRCODE_PHY (RKPM_BOOTRAM_PHYS + RKPM_BOOT_DDRCODE_OFFSET) +#define RKPM_BOOT_DDRCODE_BASE (RKPM_BOOTRAM_BASE + RKPM_BOOT_DDRCODE_OFFSET) + +/*#define RKPM_BOOT_CPUSP_PHY (RKPM_BOOTRAM_PHYS+((RKPM_BOOTRAM_SIZE-1)&~0x7))*/ +#define RKPM_BOOT_CPUSP_PHY (0x00 + ((RKPM_BOOTRAM_SIZE - 1) & (~(0x7)))) + +/*the value is used to control cpu resume flow*/ +static u32 sleep_resume_data[RKPM_BOOTDATA_ARR_SIZE]; +static char *resume_data_base = (char *)(RKPM_BOOT_DATA_BASE); +/*static char *resume_data_phy= (char *)( RKPM_BOOT_DATA_PHY);*/ + +/*****save boot sram**********************/ +#define BOOT_RAM_SAVE_SIZE (RKPM_BOOTRAM_SIZE + 4 * 10) +#define INT_RAM_SIZE (64 * 1024) +static char boot_ram_data[BOOT_RAM_SAVE_SIZE];/*8K + 40byte*/ +static char int_ram_data[INT_RAM_SIZE]; +/******resume code ***************** +#define RKPM_BOOT_CODE_OFFSET (0x00) +#define RKPM_BOOT_CODE_PHY (RKPM_BOOTRAM_PHYS + PM_BOOT_CODE_OFFSET) +#define RKPM_BOOT_CODE_BASE (RKPM_BOOTRAM_BASE + PM_BOOT_CODE_OFFSET) +#define RKPM_BOOT_CODE_SIZE (0x100) +**************************************************/ +extern void rkpm_slp_cpu_resume(void); + +static void sram_data_for_sleep(char *boot_save, char *int_save, u32 flag) +{ + char *addr_base, *addr_phy, *data_src, *data_dst; + u32 sr_size, data_size; + + addr_base = (char *)RKPM_BOOTRAM_BASE; + addr_phy = (char *)RKPM_BOOTRAM_PHYS; + sr_size = RKPM_BOOTRAM_SIZE; /*SZ8k*/ + /**********save boot sarm***********************************/ + if (boot_save) + memcpy(boot_save, addr_base, sr_size); + /**********move resume code and data to boot sram*************/ + data_dst = (char *)RKPM_BOOT_CODE_BASE; + data_src = (char *)rkpm_slp_cpu_resume; + data_size = RKPM_BOOT_CODE_SIZE; + + memcpy(data_dst, data_src, data_size); + data_dst = (char *)resume_data_base; + data_src = (char *)sleep_resume_data; + data_size = sizeof(sleep_resume_data); + memcpy((char *)data_dst, (char *)data_src, data_size); +} +static u32 rk312x_powermode; +static u32 pmu_pwrmode_con; +static u32 gpio_pmic_sleep_mode; +static u32 grf_soc_con0; +static u32 pmu_wakeup_conf; +static u32 pmic_sleep_gpio; + +static u32 rkpm_slp_mode_set(u32 ctrbits) +{ + u32 pwr_mode_config; + + if ((RKPM_CTR_ARMOFF_LPMD & ctrbits) == 0) + return 0; + + pmu_wakeup_conf = pmu_readl(RK312X_PMU_WAKEUP_CFG); + /*grf_soc_con0 = grf_readl(GRF_SOC_CON0);*/ + /*grf_writel((1 << SOC_REMAP) + | (1 << (SOC_REMAP + 16)), GRF_SOC_CON0);*/ + /*grf_gpio1a_iomux = grf_readl(0x00b8);*/ + /*grf_writel(0x00030003, 0xb8);*/ + + pmu_writel(0x01, RK312X_PMU_WAKEUP_CFG); + /* arm interrupt wake-up enable*/ + /*grf_writel((1 << SOC_REMAP) + | (1 << (SOC_REMAP + 16)), GRF_SOC_CON0);*/ + /*remap bit control, when soc_remap = 1, the bootrom is mapped to + address 0x10100000,and internal memory is mapped to address 0x0.*/ + + /*grf_writel(0X00030002, 0xb4); + rk3126 GPIO1A1 : RK3128 GPIO3C1 iomux pmic-sleep*/ + if ((pmic_sleep_gpio == 0) || (pmic_sleep_gpio == 0x1a10)) { + gpio_pmic_sleep_mode = grf_readl(0xb8); + grf_writel(0X000C000C, 0xb8); + } + /*rk3126 GPIO1A1 : RK3128 GPIO3C1 iomux pmic-sleep*/ + if (pmic_sleep_gpio == 0x3c10) { + gpio_pmic_sleep_mode = grf_readl(0xe4); + grf_writel(0X000C0004, 0xe4); + } + /*rk3126 GPIO3C1 : RK3128 GPIO3C1 iomux pmic-sleep*/ + + pwr_mode_config = BIT(pmu_power_mode_en) | BIT(pmu_global_int_disable); + pmu_pwrmode_con = pmu_readl(RK312X_PMU_PWRMODE_CON); + if (rkpm_chk_val_ctrbits(ctrbits, RKPM_CTR_IDLEAUTO_MD)) { + rkpm_ddr_printascii("-autoidle-"); + pwr_mode_config |= BIT(pmu_clk_core_src_gate_en); + } else if (rkpm_chk_val_ctrbits(ctrbits, RKPM_CTR_ARMDP_LPMD)) { + rkpm_ddr_printascii("-armdp-"); + pwr_mode_config |= BIT(pmu_core_pd_en); + } else if (rkpm_chk_val_ctrbits(ctrbits, RKPM_CTR_ARMOFF_LPMD)) { + rkpm_ddr_printascii("-armoff-"); + /*arm power off */ + pwr_mode_config |= 0 + |BIT(pmu_clk_core_src_gate_en) + |BIT(pmu_clk_bus_src_gate_en) + | BIT(pmu_core_pd_en) + /* | BIT(pmu_use_if)//aaa*/ + | BIT(pmu_sref_enter_en) + |BIT(pmu_int_en) + /* | BIT(pmu_wait_osc_24m)*/ + | BIT(pmu_ddr_gating_en) + | BIT(pmu_ddr0io_ret_de_req) + | BIT(pmu_clr_core) + | BIT(pmu_clr_crypto) + | BIT(pmu_clr_sys) + /*| BIT(pmu_clr_vio)*/ + /*| BIT(pmu_clr_video)*/ + | BIT(pmu_clr_peri) + | BIT(pmu_clr_msch) + /*| BIT(pmu_clr_gpu) */ + ; + } + pmu_writel(32 * 30, RK312X_PMU_OSC_CNT); + pmu_writel(pwr_mode_config, RK312X_PMU_PWRMODE_CON); + rk312x_powermode = pwr_mode_config; + return pmu_readl(RK312X_PMU_PWRMODE_CON); +} + +static void sram_code_data_save(u32 pwrmode) +{ + sleep_resume_data[RKPM_BOOTDATA_L2LTY_F] = 0; + sleep_resume_data[RKPM_BOOTDATA_ARM_ERRATA_818325_F] = 0; + sleep_resume_data[RKPM_BOOTDATA_DDR_F] = 0; + sleep_resume_data[RKPM_BOOTDATA_CPUSP] = RKPM_BOOT_CPUSP_PHY; + /*in sys resume ,ddr is need resume*/ + sleep_resume_data[RKPM_BOOTDATA_CPUCODE] = virt_to_phys(cpu_resume); + /*in sys resume ,ddr is need resume*/ + + sram_data_for_sleep(boot_ram_data, int_ram_data, 1); + flush_cache_all(); + outer_flush_all(); + local_flush_tlb_all(); +} + +#define RK_GICD_BASE (RK_GIC_VIRT) +#define RK_GICC_BASE (RK_GIC_VIRT+RK312X_GIC_DIST_SIZE) +#define PM_IRQN_START 32 +#define PM_IRQN_END 107 +#define gic_reg_dump(a, b, c) {} +static u32 slp_gic_save[260+50]; + +static void rkpm_gic_dist_save(u32 *context) +{ + int i = 0, j, irqstart = 0; + unsigned int gic_irqs; + + gic_irqs = readl_relaxed(RK_GICD_BASE + GIC_DIST_CTR) & 0x1f; + gic_irqs = (gic_irqs + 1) * 32; + if (gic_irqs > 1020) + gic_irqs = 1020; + + irqstart = PM_IRQN_START; + + i = 0; + + for (j = irqstart; j < gic_irqs; j += 16) + context[i++] = readl_relaxed(RK_GICD_BASE + + GIC_DIST_CONFIG + (j * 4) / 16); + gic_reg_dump("gic level", j, RK_GICD_BASE + GIC_DIST_CONFIG); + + /* + * Set all global interrupts to this CPU only. + */ + for (j = 0; j < gic_irqs; j += 4) + context[i++] = readl_relaxed(RK_GICD_BASE + + GIC_DIST_TARGET + (j * 4) / 4); + gic_reg_dump("gic trig", j, RK_GICD_BASE + GIC_DIST_TARGET); + + for (j = 0; j < gic_irqs; j += 4) + context[i++] = readl_relaxed(RK_GICD_BASE + + GIC_DIST_PRI + (j * 4) / 4); + gic_reg_dump("gic pri", j, RK_GICD_BASE + GIC_DIST_PRI); + + for (j = 0; j < gic_irqs; j += 32) + context[i++] = readl_relaxed(RK_GICD_BASE + + GIC_DIST_IGROUP + (j * 4) / 32); + gic_reg_dump("gic secure", j, RK_GICD_BASE + 0x80); + + for (j = irqstart; j < gic_irqs; j += 32) + context[i++] = readl_relaxed(RK_GICD_BASE + + GIC_DIST_PENDING_SET + (j * 4) / 32); + gic_reg_dump("gic PENDING", j, RK_GICD_BASE + + GIC_DIST_PENDING_SET); + + for (j = 0; j < gic_irqs; j += 32) + context[i++] = readl_relaxed(RK_GICD_BASE + + GIC_DIST_ENABLE_SET + (j * 4) / 32); + gic_reg_dump("gic en", j, RK_GICD_BASE + GIC_DIST_ENABLE_SET); + gic_reg_dump("gicc", 0x1c, RK_GICC_BASE); + gic_reg_dump("giccfc", 0, RK_GICC_BASE + 0xfc); + + context[i++] = readl_relaxed(RK_GICC_BASE + GIC_CPU_PRIMASK); + context[i++] = readl_relaxed(RK_GICD_BASE + GIC_DIST_CTRL); + context[i++] = readl_relaxed(RK_GICC_BASE + GIC_CPU_CTRL); + /* + context[i++]=readl_relaxed(RK_GICC_BASE + GIC_CPU_BINPOINT); + context[i++]=readl_relaxed(RK_GICC_BASE + GIC_CPU_PRIMASK); + context[i++]=readl_relaxed(RK_GICC_BASE + GIC_DIST_SOFTINT); + context[i++]=readl_relaxed(RK_GICC_BASE + GIC_CPU_CTRL); + context[i++]=readl_relaxed(RK_GICD_BASE + GIC_DIST_CTRL); + */ + for (j = irqstart; j < gic_irqs; j += 32) { + writel_relaxed(0xffffffff, RK_GICD_BASE + + GIC_DIST_ENABLE_CLEAR + j * 4 / 32); + dsb(); + } + writel_relaxed(0xffff0000, RK_GICD_BASE + GIC_DIST_ENABLE_CLEAR); + writel_relaxed(0x0000ffff, RK_GICD_BASE + GIC_DIST_ENABLE_SET); + + writel_relaxed(0, RK_GICC_BASE + GIC_CPU_CTRL); + writel_relaxed(0, RK_GICD_BASE + GIC_DIST_CTRL); +} +static void rkpm_peri_save(u32 power_mode) +{ + rkpm_gic_dist_save(&slp_gic_save[0]); +} + +static void rkpm_save_setting(u32 ctrbits) +{ + u32 pd_cpu; + + if ((RKPM_CTR_ARMOFF_LPMD & ctrbits) == 0) + return; + + rkpm_slp_mode_set(ctrbits); + if (rk312x_powermode & BIT(pmu_power_mode_en)) { + sram_code_data_save(rk312x_powermode); + rkpm_peri_save(rk312x_powermode); + } + grf_soc_con0 = grf_readl(GRF_SOC_CON0); + + grf_writel((1 << SOC_REMAP) | (1 << (SOC_REMAP + 16)), GRF_SOC_CON0); + + for (pd_cpu = PD_CPU_1; pd_cpu <= PD_CPU_3; pd_cpu++) { + writel_relaxed(0x20000 << (pd_cpu - PD_CPU_1), + RK_CRU_VIRT + RK312X_CRU_SOFTRSTS_CON(0)); + dsb(); + udelay(10); + } +} + +#define UART_DLL 0 /* Out: Divisor Latch Low */ +#define UART_DLM 1 /* Out: Divisor Latch High */ +#define UART_IER 1 +#define UART_FCR 2 +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_MCR 4 + +void slp312x_uartdbg_resume(void) +{ + void __iomem *b_addr = RK_DEBUG_UART_VIRT; + u32 pclk_id = RK312X_CLKGATE_PCLK_UART2; + u32 clk_id = (RK312X_CLKGATE_UART0_SRC + 2 * 2); + u32 gate_reg[2]; + u32 rfl_reg, lsr_reg; + + gate_reg[0] = cru_readl(RK312X_CRU_GATEID_CONS(pclk_id)); + gate_reg[1] = cru_readl(RK312X_CRU_GATEID_CONS(clk_id)); + + RK312X_CRU_UNGATING_OPS(pclk_id); + grf_writel(0x00f00000, 0x00c0); + + do { + cru_writel(CRU_W_MSK_SETBITS(0x2, 8, 0x3) + , RK312X_CRU_CLKSELS_CON(16)); + cru_writel(0|CRU_W_MSK_SETBITS(1, 9, 0x1) + , RK312X_CRU_SOFTRSTS_CON(2)); + dsb(); + dsb(); + rkpm_udelay(10); + cru_writel(0 | CRU_W_MSK_SETBITS(0, 9, 0x1) + , RK312X_CRU_SOFTRSTS_CON(2)); + + reg_writel(0x83, b_addr + UART_LCR*4); + + reg_writel(0xd, b_addr + UART_DLL*4); + reg_writel(0x0, b_addr + UART_DLM*4); + + reg_writel(0x3, b_addr + UART_LCR*4); + + reg_writel(0x5, b_addr + UART_IER*4); + reg_writel(0xc1, b_addr + UART_FCR*4); + + rfl_reg = readl_relaxed(b_addr + 0x84); + lsr_reg = readl_relaxed(b_addr + 0x14); + } while ((rfl_reg & 0x1f) || (lsr_reg & 0xf)); + + cru_writel(CRU_W_MSK_SETBITS(0x2, 8, 0x3), RK312X_CRU_CLKSELS_CON(16)); + + grf_writel(0x00f000a0, 0x00c0); + + cru_writel(gate_reg[0] | CRU_W_MSK(pclk_id % 16, 0x1) + , RK312X_CRU_GATEID_CONS(pclk_id)); + cru_writel(gate_reg[1] | CRU_W_MSK(clk_id % 16, 0x1) + , RK312X_CRU_GATEID_CONS(clk_id)); +} + +static void rkpm_gic_dist_resume(u32 *context) +{ + int i = 0, j, irqstart = 0; + unsigned int gic_irqs; + + gic_irqs = readl_relaxed(RK_GICD_BASE + GIC_DIST_CTR) & 0x1f; + gic_irqs = (gic_irqs + 1) * 32; + if (gic_irqs > 1020) + gic_irqs = 1020; + + irqstart = PM_IRQN_START; + + writel_relaxed(0, RK_GICC_BASE + GIC_CPU_CTRL); + dsb(); + writel_relaxed(0, RK_GICD_BASE + GIC_DIST_CTRL); + dsb(); + for (j = irqstart; j < gic_irqs; j += 32) { + writel_relaxed(0xffffffff, RK_GICD_BASE + + GIC_DIST_ENABLE_CLEAR + j * 4 / 32); + dsb(); + } + + i = 0; + + for (j = irqstart; j < gic_irqs; j += 16) { + writel_relaxed(context[i++], RK_GICD_BASE + + GIC_DIST_CONFIG + j * 4 / 16); + dsb(); + } + gic_reg_dump("gic level", j, RK_GICD_BASE + GIC_DIST_CONFIG); + + /* + * Set all global interrupts to this CPU only. + */ + for (j = 0; j < gic_irqs; j += 4) { + writel_relaxed(context[i++], RK_GICD_BASE + + GIC_DIST_TARGET + (j * 4) / 4); + dsb(); + } + gic_reg_dump("gic target", j, RK_GICD_BASE + GIC_DIST_TARGET); + + for (j = 0; j < gic_irqs; j += 4) { + writel_relaxed(context[i++], RK_GICD_BASE + + GIC_DIST_PRI + (j * 4) / 4); + dsb(); + } + gic_reg_dump("gic pri", j, RK_GICD_BASE + GIC_DIST_PRI); + + for (j = 0; j < gic_irqs; j += 32) { + writel_relaxed(context[i++], RK_GICD_BASE + + GIC_DIST_IGROUP + (j * 4) / 32); + dsb(); + } + gic_reg_dump("gic secu", j, RK_GICD_BASE + 0x80); + + for (j = irqstart; j < gic_irqs; j += 32) { + /*writel_relaxed(context[i++], + RK_GICD_BASE + GIC_DIST_PENDING_SET + j * 4 / 32);*/ + i++; + dsb(); + } + + gic_reg_dump("gic pending", j, RK_GICD_BASE + GIC_DIST_PENDING_SET); + + if (0) { + for (j = 0; j < gic_irqs; j += 32) { + writel_relaxed(context[i++], RK_GICD_BASE + + GIC_DIST_ENABLE_CLEAR + j * 4 / 32); + dsb(); + } + gic_reg_dump("gic disable", j, RK_GICD_BASE + + GIC_DIST_ENABLE_CLEAR); + } else { + for (j = irqstart; j < gic_irqs; j += 32) + writel_relaxed(0xffffffff, RK_GICD_BASE + + GIC_DIST_ENABLE_CLEAR + j * 4 / 32); + writel_relaxed(0xffff0000, RK_GICD_BASE + + GIC_DIST_ENABLE_CLEAR); + writel_relaxed(0x0000ffff, RK_GICD_BASE + GIC_DIST_ENABLE_SET); + } + + /*enable*/ + for (j = 0; j < gic_irqs; j += 32) { + writel_relaxed(context[i++], RK_GICD_BASE + + GIC_DIST_ENABLE_SET + (j * 4) / 32); + dsb(); + } + + gic_reg_dump("gic enable", j, RK_GICD_BASE + GIC_DIST_ENABLE_SET); + + writel_relaxed(context[i++], RK_GICC_BASE + GIC_CPU_PRIMASK); + writel_relaxed(context[i++], RK_GICD_BASE + GIC_DIST_CTRL); + writel_relaxed(context[i++], RK_GICC_BASE + GIC_CPU_CTRL); + + gic_reg_dump("gicc", 0x1c, RK_GICC_BASE); + gic_reg_dump("giccfc", 0, RK_GICC_BASE + 0xfc); +} + +static void sram_data_resume(char *boot_save, char *int_save, u32 flag) +{ + char *addr_base, *addr_phy; + u32 sr_size; + + addr_base = (char *)RKPM_BOOTRAM_BASE; + addr_phy = (char *)RKPM_BOOTRAM_PHYS; + sr_size = RKPM_BOOTRAM_SIZE; + /* save boot sram*/ + if (boot_save) + memcpy(addr_base, boot_save, sr_size); + + flush_icache_range((unsigned long)addr_base + , (unsigned long)addr_base + sr_size); + outer_clean_range((phys_addr_t) addr_phy + , (phys_addr_t)addr_phy+sr_size); +} + +static inline void sram_code_data_resume(u32 pwrmode) +{ + sram_data_resume(boot_ram_data + , int_ram_data, sleep_resume_data[RKPM_BOOTDATA_DDR_F]); +} + +static inline void rkpm_slp_mode_set_resume(void) +{ + u32 pd_cpu; + + pmu_writel(pmu_wakeup_conf, RK312X_PMU_WAKEUP_CFG); + /* arm interrupt wake-up enable*/ + pmu_writel(pmu_pwrmode_con, RK312X_PMU_PWRMODE_CON); + + for (pd_cpu = PD_CPU_1; pd_cpu <= PD_CPU_3; pd_cpu++) { + writel_relaxed(0x20002 << (pd_cpu - PD_CPU_1), + RK_CRU_VIRT + RK312X_CRU_SOFTRSTS_CON(0)); + dsb(); + udelay(10); + } + + grf_writel(grf_soc_con0 | (1 << (SOC_REMAP + 16)), GRF_SOC_CON0); + + if ((pmic_sleep_gpio == 0) || (pmic_sleep_gpio == 0x1a10)) + grf_writel(0X000C000C | gpio_pmic_sleep_mode, 0xb8); + /*rk3126 GPIO1A1 : RK3128 GPIO3C1 iomux pmic-sleep*/ + if (pmic_sleep_gpio == 0x3c10) + grf_writel(0X000C0004 | gpio_pmic_sleep_mode, 0xe4); +} + +void fiq_glue_resume(void); + +static inline void rkpm_peri_resume(u32 power_mode) +{ + rkpm_gic_dist_resume(&slp_gic_save[0]); + fiq_glue_resume(); +} + +static void rkpm_save_setting_resume(void) +{ + if (rk312x_powermode == 0) + return; + + rkpm_slp_mode_set_resume(); + if (rk312x_powermode & BIT(pmu_power_mode_en)) { + rkpm_peri_resume(rk312x_powermode); + sram_code_data_resume(rk312x_powermode); + } +} +static inline void rkpm_peri_resume_first(u32 power_mode) +{ + slp312x_uartdbg_resume(); +} +static void rkpm_slp_setting(void) +{ + rk_usb_power_down(); +} +static void rkpm_save_setting_resume_first(void) +{ + rk_usb_power_up(); + rkpm_peri_resume_first(pmu_pwrmode_con); +} +static u32 clk_ungt_msk[RK312X_CRU_CLKGATES_CON_CNT]; +/*first clk gating setting*/ + +static u32 clk_ungt_msk_1[RK312X_CRU_CLKGATES_CON_CNT]; +/* first clk gating setting*/ + +static u32 clk_ungt_save[RK312X_CRU_CLKGATES_CON_CNT]; +/*first clk gating value saveing*/ + +static u32 *p_rkpm_clkgt_last_set; +#define CLK_MSK_GATING(msk, con) cru_writel((msk << 16) | 0xffff, con) +#define CLK_MSK_UNGATING(msk, con) cru_writel(((~msk) << 16) | 0xffff, con) + +static void gtclks_suspend(void) +{ + int i; + + for (i = 0; i < RK312X_CRU_CLKGATES_CON_CNT; i++) { + clk_ungt_save[i] = cru_readl(RK312X_CRU_CLKGATES_CON(i)); + CLK_MSK_UNGATING(clk_ungt_msk[i], RK312X_CRU_CLKGATES_CON(i)); + } +} + +static void gtclks_resume(void) +{ + int i; + + for (i = 0; i < RK312X_CRU_CLKGATES_CON_CNT; i++) + cru_writel(clk_ungt_save[i] | 0xffff0000 + , RK312X_CRU_CLKGATES_CON(i)); +} +static void clks_gating_suspend_init(void) +{ + p_rkpm_clkgt_last_set = &clk_ungt_msk_1[0]; + if (clk_suspend_clkgt_info_get(clk_ungt_msk, p_rkpm_clkgt_last_set + , RK312X_CRU_CLKGATES_CON_CNT) == RK312X_CRU_CLKGATES_CON(0)) + rkpm_set_ops_gtclks(gtclks_suspend, gtclks_resume); +} +static void pmic_sleep_gpio_get_dts_info(struct device_node *parent) +{ + struct property *prop; + + prop = of_find_property(parent, "rockchip,pmic-suspend_gpios", NULL); + if (!prop) + return; + if (!prop->value) + return; + + of_property_read_u32_array(parent, "rockchip,pmic-suspend_gpios" + , &pmic_sleep_gpio, 1); +} + + +static void __init rk312x_suspend_init(void) +{ + struct device_node *parent; + u32 pm_ctrbits; + + pr_info("%s\n", __func__); + parent = of_find_node_by_name(NULL, "rockchip_suspend"); + + if (IS_ERR_OR_NULL(parent)) { + PM_ERR("%s dev node err\n", __func__); + return; + } + + if (of_property_read_u32_array(parent + , "rockchip,ctrbits", &pm_ctrbits, 1)) { + PM_ERR("%s:get pm ctr error\n", __func__); + return; + } + pmic_sleep_gpio_get_dts_info(parent); + rkpm_set_ctrbits(pm_ctrbits); + clks_gating_suspend_init(); + rkpm_set_ops_prepare_finish(rkpm_prepare, rkpm_finish); + rkpm_set_ops_plls(pm_plls_suspend, pm_plls_resume); + rkpm_set_ops_save_setting(rkpm_save_setting + , rkpm_save_setting_resume); + rkpm_set_ops_regs_sleep(rkpm_slp_setting + , rkpm_save_setting_resume_first); + rkpm_set_ops_printch(ddr_printch); +} \ No newline at end of file diff --git a/arch/arm/mach-rockchip/rk312x.c b/arch/arm/mach-rockchip/rk312x.c index f0553914a1bb..4286771c5a2f 100755 --- a/arch/arm/mach-rockchip/rk312x.c +++ b/arch/arm/mach-rockchip/rk312x.c @@ -30,7 +30,7 @@ #include #include #include -#include +/*#include */ #include #include #include @@ -39,7 +39,7 @@ #define CPU 312x #include "sram.h" #include "pm.h" - +#include "pm-rk312x.c" #define RK312X_DEVICE(name) \ { \ .virtual = (unsigned long) RK_##name##_VIRT, \ @@ -131,10 +131,148 @@ static void __init rk3128_dt_map_io(void) rk312x_dt_map_io(); } +static DEFINE_SPINLOCK(pmu_idle_lock); +static const u8 pmu_idle_map[] = { + [IDLE_REQ_PERI] = 0, + [IDLE_REQ_VIDEO] = 1, + [IDLE_REQ_VIO] = 2, + [IDLE_REQ_GPU] = 3, + [IDLE_REQ_CORE] = 4, + [IDLE_REQ_SYS] = 5, + [IDLE_REQ_MSCH] = 6, + [IDLE_REQ_CRYPTO] = 7, + +}; +static int rk312x_pmu_set_idle_request(enum pmu_idle_req req, bool idle) +{ + u32 val; + unsigned long flags; + u32 bit = pmu_idle_map[req]; + u32 idle_mask = BIT(bit) | BIT(bit + 16); + u32 idle_target = (idle << bit) | (idle << (bit + 16)); + u32 mask = BIT(bit); + + spin_lock_irqsave(&pmu_idle_lock, flags); + val = pmu_readl(RK312X_PMU_IDLE_REQ); + if (idle) + val |= mask; + else + val &= ~mask; + pmu_writel(val, RK312X_PMU_IDLE_REQ); + dsb(); + + while (((pmu_readl(RK312X_PMU_IDLE_ST) & idle_mask) != idle_target)) + ; + spin_unlock_irqrestore(&pmu_idle_lock, flags); + return 0; +} +static const u8 pmu_pd_map[] = { + [PD_GPU] = 1, + [PD_VIDEO] = 2, + [PD_VIO] = 3, +}; + +static const u8 pmu_st_map[] = { + [PD_GPU] = 1, + [PD_VIDEO] = 2, + [PD_VIO] = 3, +}; + +static noinline void rk312x_do_pmu_set_power_domain(enum pmu_power_domain domain + , bool on) +{ + u8 pd = pmu_pd_map[domain]; + u32 val = pmu_readl(RK312X_PMU_PWRDN_CON); + + if (on) + val &= ~BIT(pd); + else + val |= BIT(pd); + pmu_writel(val, RK312X_PMU_PWRDN_CON); + dsb(); + + while ((pmu_readl(RK312X_PMU_PWRDN_ST) & BIT(pmu_st_map[domain])) == on) + ; +} +static bool rk312x_pmu_power_domain_is_on(enum pmu_power_domain pd) +{ + /*1"b0: power on, 1'b1: power off*/ + return !(pmu_readl(RK312X_PMU_PWRDN_ST) & BIT(pmu_st_map[pd])); +} +static DEFINE_SPINLOCK(pmu_pd_lock); +static u32 rga_qos[RK312X_CPU_AXI_QOS_NUM_REGS]; +static u32 ebc_qos[RK312X_CPU_AXI_QOS_NUM_REGS]; +static u32 iep_qos[RK312X_CPU_AXI_QOS_NUM_REGS]; +static u32 lcdc0_qos[RK312X_CPU_AXI_QOS_NUM_REGS]; +static u32 vip0_qos[RK312X_CPU_AXI_QOS_NUM_REGS]; +static u32 gpu_qos[RK312X_CPU_AXI_QOS_NUM_REGS]; +static u32 video_qos[RK312X_CPU_AXI_QOS_NUM_REGS]; + +#define SAVE_QOS(array, NAME) RK312X_CPU_AXI_SAVE_QOS(array, RK312X_CPU_AXI_##NAME##_QOS_VIRT) +#define RESTORE_QOS(array, NAME) RK312X_CPU_AXI_RESTORE_QOS(array, RK312X_CPU_AXI_##NAME##_QOS_VIRT) + +static int rk312x_pmu_set_power_domain(enum pmu_power_domain pd, bool on) +{ + unsigned long flags; + + spin_lock_irqsave(&pmu_pd_lock, flags); + if (rk312x_pmu_power_domain_is_on(pd) == on) + goto out; + if (!on) { + if (pd == PD_GPU) { + SAVE_QOS(gpu_qos, GPU); + rk312x_pmu_set_idle_request(IDLE_REQ_GPU, true); + } else if (pd == PD_VIO) { + SAVE_QOS(rga_qos, VIO_RGA); + SAVE_QOS(ebc_qos, VIO_EBC); + SAVE_QOS(iep_qos, VIO_IEP); + SAVE_QOS(lcdc0_qos, VIO_LCDC0); + SAVE_QOS(vip0_qos, VIO_VIP0); + rk312x_pmu_set_idle_request(IDLE_REQ_VIO, true); + } else if (pd == PD_VIDEO) { + SAVE_QOS(video_qos, VIDEO); + rk312x_pmu_set_idle_request(IDLE_REQ_VIDEO, true); + } + } + + rk312x_do_pmu_set_power_domain(pd, on); + + if (on) { + if (pd == PD_GPU) { + rk312x_pmu_set_idle_request(IDLE_REQ_GPU, false); + RESTORE_QOS(gpu_qos, GPU); + } else if (pd == PD_VIO) { + rk312x_pmu_set_idle_request(IDLE_REQ_VIO, false); + RESTORE_QOS(rga_qos, VIO_RGA); + RESTORE_QOS(ebc_qos, VIO_EBC); + RESTORE_QOS(iep_qos, VIO_IEP); + RESTORE_QOS(lcdc0_qos, VIO_LCDC0); + RESTORE_QOS(vip0_qos, VIO_VIP0); + } else if (pd == PD_VIDEO) { + rk312x_pmu_set_idle_request(IDLE_REQ_VIDEO, false); + RESTORE_QOS(video_qos, VIDEO); + } + } +out: + spin_unlock_irqrestore(&pmu_pd_lock, flags); + + return 0; +} extern void secondary_startup(void); static int rk312x_sys_set_power_domain(enum pmu_power_domain pd, bool on) { + u32 clks_save[RK312X_CRU_CLKGATES_CON_CNT]; + u32 clks_ungating[RK312X_CRU_CLKGATES_CON_CNT]; + u32 i, ret = 0; + + for (i = 0; i < RK312X_CRU_CLKGATES_CON_CNT; i++) { + clks_save[i] = cru_readl(RK312X_CRU_CLKGATES_CON(i)); + clks_ungating[i] = 0; + } + for (i = 0; i < RK312X_CRU_CLKGATES_CON_CNT; i++) + cru_writel(0xffff0000, RK312X_CRU_CLKGATES_CON(i)); + if (on) { #ifdef CONFIG_SMP if (pd >= PD_CPU_1 && pd <= PD_CPU_3) { @@ -158,17 +296,15 @@ static int rk312x_sys_set_power_domain(enum pmu_power_domain pd, bool on) #endif } - return 0; -} + if (((pd == PD_GPU) || (pd == PD_VIO) || (pd == PD_VIDEO))) + ret = rk312x_pmu_set_power_domain(pd, on); -static bool rk312x_pmu_power_domain_is_on(enum pmu_power_domain pd) -{ - return 1; -} + for (i = 0; i < RK312X_CRU_CLKGATES_CON_CNT; i++) { + cru_writel(clks_save[i] | 0xffff0000 + , RK312X_CRU_CLKGATES_CON(i)); + } -static int rk312x_pmu_set_idle_request(enum pmu_idle_req req, bool idle) -{ - return 0; + return ret; } static void __init rk312x_dt_init_timer(void) @@ -186,9 +322,14 @@ static void __init rk312x_reserve(void) /* reserve memory for ION */ rockchip_ion_reserve(); } - +#ifdef CONFIG_PM +static void __init rk321x_init_suspend(void); +#endif static void __init rk312x_init_late(void) { +#ifdef CONFIG_PM + rk321x_init_suspend(); +#endif } static void rk312x_restart(char mode, const char *cmd) @@ -197,8 +338,8 @@ static void rk312x_restart(char mode, const char *cmd) rockchip_restart_get_boot_mode(cmd, &boot_flag, &boot_mode); - writel_relaxed(boot_flag, RK_GRF_VIRT + RK312X_GRF_OS_REG4); // for loader - writel_relaxed(boot_mode, RK_GRF_VIRT + RK312X_GRF_OS_REG5); // for linux + writel_relaxed(boot_flag, RK_GRF_VIRT + RK312X_GRF_OS_REG4); + writel_relaxed(boot_mode, RK_GRF_VIRT + RK312X_GRF_OS_REG5); dsb(); /* pll enter slow mode */ @@ -257,6 +398,7 @@ static int __init rk312x_pie_init(void) return 0; } arch_initcall(rk312x_pie_init); + #include "ddr_rk3126.c" static int __init rk312x_ddr_init(void) { @@ -269,3 +411,37 @@ static int __init rk312x_ddr_init(void) return 0; } arch_initcall_sync(rk312x_ddr_init); + +#ifdef CONFIG_PM +static u32 rk_pmu_pwrdn_st; +static inline void rk_pm_soc_pd_suspend(void) +{ + rk_pmu_pwrdn_st = pmu_readl(RK312X_PMU_PWRDN_ST); + if (!(rk_pmu_pwrdn_st & BIT(pmu_st_map[PD_GPU]))) + rk312x_sys_set_power_domain(PD_GPU, false); + + if (!(rk_pmu_pwrdn_st & BIT(pmu_st_map[PD_VIO]))) + rk312x_sys_set_power_domain(PD_VIO, false); + + if (!(rk_pmu_pwrdn_st & BIT(pmu_st_map[PD_VIDEO]))) + rk312x_sys_set_power_domain(PD_VIDEO, false); +} +static inline void rk_pm_soc_pd_resume(void) +{ + if (!(rk_pmu_pwrdn_st & BIT(pmu_st_map[PD_VIDEO]))) + rk312x_sys_set_power_domain(PD_VIDEO, true); + + if (!(rk_pmu_pwrdn_st & BIT(pmu_st_map[PD_VIO]))) + rk312x_sys_set_power_domain(PD_VIO, true); + + if (!(rk_pmu_pwrdn_st & BIT(pmu_st_map[PD_GPU]))) + rk312x_sys_set_power_domain(PD_GPU, true); +} +static void __init rk321x_init_suspend(void) +{ + pr_info("%s\n", __func__); + rockchip_suspend_init(); + rk312x_suspend_init(); + rkpm_set_ops_pwr_dmns(rk_pm_soc_pd_suspend, rk_pm_soc_pd_resume); +} +#endif diff --git a/include/linux/rockchip/cru.h b/include/linux/rockchip/cru.h index 921cee43360a..2d8c941f3eb3 100755 --- a/include/linux/rockchip/cru.h +++ b/include/linux/rockchip/cru.h @@ -161,7 +161,6 @@ static inline void rk3288_cru_set_soft_reset(u32 idx, bool on) #define RK3036_CRU_SOFTRSTS_CON_CNT (9) #define RK3036_CRU_SOFTRSTS_CON(i) (RK3036_CRU_SOFTRST_CON + ((i) * 4)) -#define RK312X_CRU_MODE_CON 0x0040 #define RK312X_PLL_CONS(id, i) ((id) * 0x10 + ((i) * 4)) #define RK312X_CRU_GLB_SRST_FST_VALUE 0x00100 @@ -176,12 +175,24 @@ static inline void rk3288_cru_set_soft_reset(u32 idx, bool on) #define RK312X_CRU_EMMC_CON0 0x01d8 #define RK312X_CRU_EMMC_CON1 0x01dc #define RK312X_CRU_PLL_PRG_EN 0x01f0 - +#define RK312X_CRU_MODE_CON 0x40 #define RK312X_CRU_RST_ST 0x00160 #define RK312X_CRU_PLL_MASK_CON 0x001f0 #define RK312X_CRU_CLKSEL_CON 0x44 #define RK312X_CRU_CLKGATE_CON 0xd0 + +#define RK312X_PLL_CONS(id, i) ((id) * 0x10 + ((i) * 4)) + +/******************PLL MODE BITS*******************/ +#define RK312X_PLLS_MODE_OFFSET(id) ((id) <= 3 ? (id * 4) : 14) +#define RK312X_PLL_MODE_MSK(id) (0x1 << RK312X_PLLS_MODE_OFFSET(id)) +#define RK312X_PLL_MODE_SLOW(id) ((0x0 << RK312X_PLLS_MODE_OFFSET(id))\ +| (0x1 << (16 + RK312X_PLLS_MODE_OFFSET(id)))) +#define RK312X_PLL_MODE_NORM(id) ((0x1 << RK312X_PLLS_MODE_OFFSET(id))\ +| (0x1 << (16 + RK312X_PLLS_MODE_OFFSET(id)))) + + #define RK312X_CRU_SOFTRST_CON 0x110 #define RK312X_CRU_CLKSELS_CON_CNT (35) @@ -193,4 +204,16 @@ static inline void rk3288_cru_set_soft_reset(u32 idx, bool on) #define RK312X_CRU_SOFTRSTS_CON_CNT (9) #define RK312X_CRU_SOFTRSTS_CON(i) (RK312X_CRU_SOFTRST_CON + ((i) * 4)) +/*******************CRU GATING*********************/ +#define RK312X_CRU_CONS_GATEID(i) (16 * (i)) +#define RK312X_CRU_GATEID_CONS(ID) (RK312X_CRU_CLKGATE_CON\ + + ((ID) / 16) * 4) + +enum rk312x_cru_clk_gate { + /* SCU CLK GATE 0 CON */ + RK312X_CLKGATE_UART0_SRC = (RK312X_CRU_CONS_GATEID(1) + 8), + RK312X_CLKGATE_PCLK_UART0 = (RK312X_CRU_CONS_GATEID(8) + 0), + RK312X_CLKGATE_PCLK_UART1, + RK312X_CLKGATE_PCLK_UART2, +}; #endif diff --git a/include/linux/rockchip/pmu.h b/include/linux/rockchip/pmu.h index 19a10a55d89a..02f379c929f8 100755 --- a/include/linux/rockchip/pmu.h +++ b/include/linux/rockchip/pmu.h @@ -67,6 +67,26 @@ #define RK3288_PMU_SYS_REG2 0x9c #define RK3288_PMU_SYS_REG3 0xa0 +#define RK312X_PMU_WAKEUP_CFG 0x00 +#define RK312X_PMU_PWRDN_CON 0x04 +#define RK312X_PMU_PWRDN_ST 0x08 +#define RK312X_PMU_IDLE_REQ 0x0C +#define RK312X_PMU_IDLE_ST 0x10 +#define RK312X_PMU_PWRMODE_CON 0x14 +#define RK312X_PMU_PWR_STATE 0x18 +#define RK312X_PMU_OSC_CNT 0x1C +#define RK312X_PMU_CORE_PWRDWN_CNT 0x20 +#define RK312X_PMU_CORE_PWRUP_CNT 0x24 +#define RK312X_PMU_SFT_CON 0x28 +#define RK312X_PMU_DDR_SREF_ST 0x2C +#define RK312X_PMU_INT_CON 0x30 +#define RK312X_PMU_INT_ST 0x34 +#define RK312X_PMU_SYS_REG0 0x38 +#define RK312X_PMU_SYS_REG1 0x3C +#define RK312X_PMU_SYS_REG2 0x40 +#define RK312X_PMU_SYS_REG3 0x44 + + enum pmu_power_domain { PD_BCPU, PD_BDSP, @@ -97,6 +117,9 @@ enum pmu_idle_req { IDLE_REQ_PERI, IDLE_REQ_VIDEO, IDLE_REQ_VIO, + IDLE_REQ_SYS, + IDLE_REQ_MSCH, + IDLE_REQ_CRYPTO, }; struct rockchip_pmu_operations { -- 2.34.1