ARM: rockchip: rk3188 add restart support
author黄涛 <huangtao@rock-chips.com>
Wed, 15 Jan 2014 08:40:50 +0000 (16:40 +0800)
committer黄涛 <huangtao@rock-chips.com>
Wed, 15 Jan 2014 08:40:50 +0000 (16:40 +0800)
arch/arm/mach-rockchip/common.c
arch/arm/mach-rockchip/common.h [new file with mode: 0644]
arch/arm/mach-rockchip/core.h [deleted file]
arch/arm/mach-rockchip/cpu_axi.h
arch/arm/mach-rockchip/cru.h [new file with mode: 0644]
arch/arm/mach-rockchip/loader.h [new file with mode: 0644]
arch/arm/mach-rockchip/platsmp.c
arch/arm/mach-rockchip/pmu.h [new file with mode: 0644]
arch/arm/mach-rockchip/rk3188.c

index 93adf5dadcf0e3a117b43c71ba583a6f32b317a3..9bf34cc7bb2bfbd4afa3da4cfec63e52aa5da5fa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 ROCKCHIP, Inc.
+ * Copyright (C) 2013-2014 ROCKCHIP, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
 #include <asm/hardware/cache-l2x0.h>
+#include "common.h"
 #include "cpu_axi.h"
+#include "loader.h"
+#include "pmu.h"
 #include "sram.h"
 
 static int __init rockchip_cpu_axi_init(void)
@@ -130,3 +133,85 @@ int __init rockchip_pie_init(void)
 
        return 0;
 }
+
+static bool is_panic = false;
+
+static int panic_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+       is_panic = true;
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+       .notifier_call  = panic_event,
+};
+
+static int boot_mode;
+
+int rockchip_boot_mode(void)
+{
+       return boot_mode;
+}
+EXPORT_SYMBOL(rockchip_boot_mode);
+
+static inline const char *boot_flag_name(u32 flag)
+{
+       flag -= SYS_KERNRL_REBOOT_FLAG;
+       switch (flag) {
+       case BOOT_NORMAL: return "NORMAL";
+       case BOOT_LOADER: return "LOADER";
+       case BOOT_MASKROM: return "MASKROM";
+       case BOOT_RECOVER: return "RECOVER";
+       case BOOT_NORECOVER: return "NORECOVER";
+       case BOOT_SECONDOS: return "SECONDOS";
+       case BOOT_WIPEDATA: return "WIPEDATA";
+       case BOOT_WIPEALL: return "WIPEALL";
+       case BOOT_CHECKIMG: return "CHECKIMG";
+       case BOOT_FASTBOOT: return "FASTBOOT";
+       default: return "";
+       }
+}
+
+static inline const char *boot_mode_name(u32 mode)
+{
+       switch (mode) {
+       case BOOT_MODE_NORMAL: return "NORMAL";
+       case BOOT_MODE_FACTORY2: return "FACTORY2";
+       case BOOT_MODE_RECOVERY: return "RECOVERY";
+       case BOOT_MODE_CHARGE: return "CHARGE";
+       case BOOT_MODE_POWER_TEST: return "POWER_TEST";
+       case BOOT_MODE_OFFMODE_CHARGING: return "OFFMODE_CHARGING";
+       case BOOT_MODE_REBOOT: return "REBOOT";
+       case BOOT_MODE_PANIC: return "PANIC";
+       case BOOT_MODE_WATCHDOG: return "WATCHDOG";
+       default: return "";
+       }
+}
+
+void __init rockchip_boot_mode_init(u32 flag, u32 mode)
+{
+       boot_mode = mode;
+       if (mode || ((flag & 0xff) && ((flag & 0xffffff00) == SYS_KERNRL_REBOOT_FLAG)))
+               printk("Boot mode: %s (%d) flag: %s (0x%08x)\n", boot_mode_name(mode), mode, boot_flag_name(flag), flag);
+       atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
+}
+
+void rockchip_restart_get_boot_mode(const char *cmd, u32 *flag, u32 *mode)
+{
+       *flag = 0;
+       *mode = BOOT_MODE_REBOOT;
+
+       if (cmd) {
+               if (!strcmp(cmd, "loader") || !strcmp(cmd, "bootloader"))
+                       *flag = SYS_LOADER_REBOOT_FLAG + BOOT_LOADER;
+               else if(!strcmp(cmd, "recovery"))
+                       *flag = SYS_LOADER_REBOOT_FLAG + BOOT_RECOVER;
+               else if (!strcmp(cmd, "charge"))
+                       *mode = BOOT_MODE_CHARGE;
+       } else {
+               if (is_panic)
+                       *mode = BOOT_MODE_PANIC;
+       }
+}
+
+struct rockchip_pmu_operations rockchip_pmu_ops;
diff --git a/arch/arm/mach-rockchip/common.h b/arch/arm/mach-rockchip/common.h
new file mode 100644 (file)
index 0000000..e4fec0b
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __MACH_ROCKCHIP_COMMON_H
+#define __MACH_ROCKCHIP_COMMON_H
+
+extern unsigned long rockchip_boot_fn;
+extern struct smp_operations rockchip_smp_ops;
+
+#define BOOT_MODE_NORMAL               0
+#define BOOT_MODE_FACTORY2             1
+#define BOOT_MODE_RECOVERY             2
+#define BOOT_MODE_CHARGE               3
+#define BOOT_MODE_POWER_TEST           4
+#define BOOT_MODE_OFFMODE_CHARGING     5
+#define BOOT_MODE_REBOOT               6
+#define BOOT_MODE_PANIC                        7
+#define BOOT_MODE_WATCHDOG             8
+
+extern int rockchip_boot_mode(void);
+extern void __init rockchip_boot_mode_init(u32 flag, u32 mode);
+extern void rockchip_restart_get_boot_mode(const char *cmd, u32 *flag, u32 *mode);
+
+#endif
diff --git a/arch/arm/mach-rockchip/core.h b/arch/arm/mach-rockchip/core.h
deleted file mode 100644 (file)
index a235685..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2013 ROCKCHIP, Inc.
- * Copyright (c) 2013 MundoReader S.L.
- * Author: Heiko Stuebner <heiko@sntech.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef __MACH_CORE_H
-#define __MACH_CORE_H
-
-extern unsigned long rockchip_boot_fn;
-
-extern struct smp_operations rockchip_smp_ops;
-
-#endif
index 0523be5a455722e69d0ec1571a058de1b90aeca4..e3beedfef4710e42f32df6056652c19743549ca1 100644 (file)
        writel_relaxed(array[3], base + CPU_AXI_QOS_SATURATION); \
 } while (0)
 
+#define RK3188_CPU_AXI_DMAC_QOS_BASE    (RK_CPU_AXI_BUS_VIRT + 0x1000)
+#define RK3188_CPU_AXI_CPU0_QOS_BASE    (RK_CPU_AXI_BUS_VIRT + 0x2000)
+#define RK3188_CPU_AXI_CPU1R_QOS_BASE   (RK_CPU_AXI_BUS_VIRT + 0x2080)
+#define RK3188_CPU_AXI_CPU1W_QOS_BASE   (RK_CPU_AXI_BUS_VIRT + 0x2100)
+#define RK3188_CPU_AXI_PERI_QOS_BASE    (RK_CPU_AXI_BUS_VIRT + 0x4000)
+#define RK3188_CPU_AXI_GPU_QOS_BASE     (RK_CPU_AXI_BUS_VIRT + 0x5000)
+#define RK3188_CPU_AXI_VPU_QOS_BASE     (RK_CPU_AXI_BUS_VIRT + 0x6000)
+#define RK3188_CPU_AXI_LCDC0_QOS_BASE   (RK_CPU_AXI_BUS_VIRT + 0x7000)
+#define RK3188_CPU_AXI_CIF0_QOS_BASE    (RK_CPU_AXI_BUS_VIRT + 0x7080)
+#define RK3188_CPU_AXI_IPP_QOS_BASE     (RK_CPU_AXI_BUS_VIRT + 0x7100)
+#define RK3188_CPU_AXI_LCDC1_QOS_BASE   (RK_CPU_AXI_BUS_VIRT + 0x7180)
+#define RK3188_CPU_AXI_CIF1_QOS_BASE    (RK_CPU_AXI_BUS_VIRT + 0x7200)
+#define RK3188_CPU_AXI_RGA_QOS_BASE     (RK_CPU_AXI_BUS_VIRT + 0x7280)
+
 #endif
diff --git a/arch/arm/mach-rockchip/cru.h b/arch/arm/mach-rockchip/cru.h
new file mode 100644 (file)
index 0000000..15092a2
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef __MACH_ROCKCHIP_CRU_H
+#define __MACH_ROCKCHIP_CRU_H
+
+enum {
+       RK3188_APLL_ID = 0,
+       RK3188_DPLL_ID,
+       RK3188_CPLL_ID,
+       RK3188_GPLL_ID,
+       RK3188_END_PLL_ID,
+};
+
+#define RK3188_CRU_MODE_CON            0x40
+#define RK3188_CRU_CLKSEL_CON          0x44
+#define RK3188_CRU_CLKGATE_CON         0xd0
+#define RK3188_CRU_GLB_SRST_FST                0x100
+#define RK3188_CRU_GLB_SRST_SND                0x104
+#define RK3188_CRU_SOFTRST_CON         0x110
+
+#define RK3188_PLL_CONS(id, i)         ((id) * 0x10 + ((i) * 4))
+
+#define RK3188_CRU_CLKSELS_CON_CNT     (35)
+#define RK3188_CRU_CLKSELS_CON(i)      (RK3188_CRU_CLKSEL_CON + ((i) * 4))
+
+#define RK3188_CRU_CLKGATES_CON_CNT    (10)
+#define RK3188_CRU_CLKGATES_CON(i)     (RK3188_CRU_CLKGATE_CON + ((i) * 4))
+
+#define RK3188_CRU_SOFTRSTS_CON_CNT    (9)
+#define RK3188_CRU_SOFTRSTS_CON(i)     (RK3188_CRU_SOFTRST_CON + ((i) * 4))
+
+#define RK3188_CRU_MISC_CON            (0x134)
+#define RK3188_CRU_GLB_CNT_TH          (0x140)
+
+/*******************MODE BITS***************************/
+
+#define RK3188_PLL_MODE_MSK(id)                (0x3 << ((id) * 4))
+#define RK3188_PLL_MODE_SLOW(id)       ((0x0<<((id)*4))|(0x3<<(16+(id)*4)))
+#define RK3188_PLL_MODE_NORM(id)       ((0x1<<((id)*4))|(0x3<<(16+(id)*4)))
+#define RK3188_PLL_MODE_DEEP(id)       ((0x2<<((id)*4))|(0x3<<(16+(id)*4)))
+
+#endif
diff --git a/arch/arm/mach-rockchip/loader.h b/arch/arm/mach-rockchip/loader.h
new file mode 100644 (file)
index 0000000..9eaa790
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __MACH_ROCKCHIP_LOADER_H
+#define __MACH_ROCKCHIP_LOADER_H
+
+#define SYS_LOADER_REBOOT_FLAG   0x5242C300  //high 24 bits is tag, low 8 bits is type
+#define SYS_KERNRL_REBOOT_FLAG   0xC3524200  //high 24 bits is tag, low 8 bits is type
+
+enum {
+    BOOT_NORMAL = 0, /* normal boot */
+    BOOT_LOADER,     /* enter loader rockusb mode */
+    BOOT_MASKROM,    /* enter maskrom rockusb mode (not support now) */
+    BOOT_RECOVER,    /* enter recover */
+    BOOT_NORECOVER,  /* do not enter recover */
+    BOOT_SECONDOS,   /* boot second OS (not support now)*/
+    BOOT_WIPEDATA,   /* enter recover and wipe data. */
+    BOOT_WIPEALL,    /* enter recover and wipe all data. */
+    BOOT_CHECKIMG,   /* check firmware img with backup part(in loader mode)*/
+    BOOT_FASTBOOT,   /* enter fast boot mode (not support now) */
+    BOOT_MAX         /* MAX VALID BOOT TYPE.*/
+};
+
+#endif
index 1c43e5df58126fbb05fc5cbbdfc60ed182908a62..fb31bb47ff65595fff30a0282148539351824e72 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 ROCKCHIP, Inc.
+ * Copyright (C) 2013-2014 ROCKCHIP, Inc.
  * Copyright (c) 2013 MundoReader S.L.
  * Author: Heiko Stuebner <heiko@sntech.de>
  *
 #include <asm/smp_plat.h>
 #include <asm/mach/map.h>
 
-#include "core.h"
+#include "common.h"
+#include "pmu.h"
 
 #define SCU_CTRL               0x00
 #define   SCU_STANDBY_EN       (1 << 5)
 
 static int ncores;
 
-/*
- * temporary PMU handling
- */
-
-#define PMU_PWRDN_CON          0x08
-#define PMU_PWRDN_ST           0x0c
-
-static void __iomem *pmu_base_addr;
-
 extern void secondary_startup(void);
 extern void v7_invalidate_l1(void);
 
@@ -61,23 +53,6 @@ static void __naked rockchip_secondary_trampoline(void)
        );
 }
 
-static inline bool pmu_power_domain_is_on(int pd)
-{
-       return !(readl_relaxed(pmu_base_addr + PMU_PWRDN_ST) & BIT(pd));
-}
-
-static void pmu_set_power_domain(int pd, bool on)
-{
-       u32 val = readl_relaxed(pmu_base_addr + PMU_PWRDN_CON);
-       if (on)
-               val &= ~BIT(pd);
-       else
-               val |=  BIT(pd);
-       writel(val, pmu_base_addr + PMU_PWRDN_CON);
-
-       while (pmu_power_domain_is_on(pd) != on) { }
-}
-
 /*
  * Handling of CPU cores
  */
@@ -92,7 +67,7 @@ static int __cpuinit rockchip_boot_secondary(unsigned int cpu,
        }
 
        /* start the core */
-       pmu_set_power_domain(0 + cpu, true);
+       rockchip_pmu_ops.set_power_domain(PD_CPU_0 + cpu, true);
 
        return 0;
 }
@@ -158,8 +133,6 @@ static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
                return;
        }
 
-       pmu_base_addr = of_iomap(node, 0);
-
        /*
         * While the number of cpus is gathered from dt, also get the number
         * of cores from the scu to verify this value when booting the cores.
@@ -174,7 +147,7 @@ static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
        for (i = 0; i < ncores; i++) {
                if (i == cpu)
                        continue;
-               pmu_set_power_domain(i, false);
+               rockchip_pmu_ops.set_power_domain(PD_CPU_0 + i, false);
        }
 
        iounmap(scu_base_addr);
diff --git a/arch/arm/mach-rockchip/pmu.h b/arch/arm/mach-rockchip/pmu.h
new file mode 100644 (file)
index 0000000..2224fae
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef __MACH_ROCKCHIP_PMU_H
+#define __MACH_ROCKCHIP_PMU_H
+
+#define RK3188_PMU_WAKEUP_CFG0          0x00
+#define RK3188_PMU_WAKEUP_CFG1          0x04
+#define RK3188_PMU_PWRDN_CON            0x08
+#define RK3188_PMU_PWRDN_ST             0x0c
+#define RK3188_PMU_INT_CON              0x10
+#define RK3188_PMU_INT_ST               0x14
+#define RK3188_PMU_MISC_CON             0x18
+#define RK3188_PMU_OSC_CNT              0x1c
+#define RK3188_PMU_PLL_CNT              0x20
+#define RK3188_PMU_PMU_CNT              0x24
+#define RK3188_PMU_DDRIO_PWRON_CNT      0x28
+#define RK3188_PMU_WAKEUP_RST_CLR_CNT   0x2c
+#define RK3188_PMU_SCU_PWRDWN_CNT       0x30
+#define RK3188_PMU_SCU_PWRUP_CNT        0x34
+#define RK3188_PMU_MISC_CON1            0x38
+#define RK3188_PMU_GPIO0_CON            0x3c
+#define RK3188_PMU_SYS_REG0             0x40
+#define RK3188_PMU_SYS_REG1             0x44
+#define RK3188_PMU_SYS_REG2             0x48
+#define RK3188_PMU_SYS_REG3             0x4c
+#define RK3188_PMU_STOP_INT_DLY         0x60
+#define RK3188_PMU_GPIO0A_PULL          0x64
+#define RK3188_PMU_GPIO0B_PULL          0x68
+
+enum pmu_power_domain {
+       PD_BCPU,
+       PD_BDSP,
+       PD_BUS,
+       PD_CPU_0,
+       PD_CPU_1,
+       PD_CPU_2,
+       PD_CPU_3,
+       PD_CS,
+       PD_GPU,
+       PD_PERI,
+       PD_SCU,
+       PD_VIDEO,
+       PD_VIO,
+};
+
+enum pmu_idle_req {
+       IDLE_REQ_ALIVE,
+       IDLE_REQ_AP2BP,
+       IDLE_REQ_BP2AP,
+       IDLE_REQ_BUS,
+       IDLE_REQ_CORE,
+       IDLE_REQ_DMA,
+       IDLE_REQ_GPU,
+       IDLE_REQ_PERI,
+       IDLE_REQ_VIDEO,
+       IDLE_REQ_VIO,
+};
+
+struct rockchip_pmu_operations {
+       int (*set_power_domain)(enum pmu_power_domain pd, bool on);
+       bool (*power_domain_is_on)(enum pmu_power_domain pd);
+       int (*set_idle_request)(enum pmu_idle_req req, bool idle);
+};
+
+extern struct rockchip_pmu_operations rockchip_pmu_ops;
+
+#endif
index 1dc806d9d5eae79b0c8d9c83e61ec6ef0f9485a9..23b5db53b98099123543cd383c675dfd93723389 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Device Tree support for Rockchip RK3188
  *
- * Copyright (C) 2013 ROCKCHIP, Inc.
+ * Copyright (C) 2013-2014 ROCKCHIP, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/of_platform.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
-#include "core.h"
+#include "common.h"
 #include "cpu.h"
 #include "cpu_axi.h"
+#include "cru.h"
 #include "grf.h"
 #include "iomap.h"
+#include "loader.h"
+#include "pmu.h"
 #include "sram.h"
 
 #define RK3188_DEVICE(name) \
@@ -89,6 +92,20 @@ static struct map_desc rk3188_io_desc[] __initdata = {
        },
 };
 
+static void __init rk3188_boot_mode_init(void)
+{
+       u32 flag = readl_relaxed(RK_PMU_VIRT + RK3188_PMU_SYS_REG0);
+       u32 mode = readl_relaxed(RK_PMU_VIRT + RK3188_PMU_SYS_REG1);
+
+       if (flag == (SYS_KERNRL_REBOOT_FLAG | BOOT_RECOVER)) {
+               mode = BOOT_MODE_RECOVERY;
+       }
+       rockchip_boot_mode_init(flag, mode);
+#ifdef CONFIG_RK29_WATCHDOG
+       writel_relaxed(BOOT_MODE_WATCHDOG, RK_PMU_VIRT + RK3188_PMU_SYS_REG1);
+#endif
+}
+
 static void __init rk3188_dt_map_io(void)
 {
        preset_lpj = 11996091ULL / 2;
@@ -104,24 +121,214 @@ static void __init rk3188_dt_map_io(void)
 
        /* rki2c is used instead of old i2c */
        writel_relaxed(0xF800F800, RK_GRF_VIRT + RK3188_GRF_SOC_CON1);
+
+       rk3188_boot_mode_init();
+}
+
+static const u8 pmu_pd_map[] = {
+       [PD_CPU_0] = 0,
+       [PD_CPU_1] = 1,
+       [PD_CPU_2] = 2,
+       [PD_CPU_3] = 3,
+       [PD_SCU] = 4,
+       [PD_BUS] = 5,
+       [PD_PERI] = 6,
+       [PD_VIO] = 7,
+       [PD_VIDEO] = 8,
+       [PD_GPU] = 9,
+       [PD_CS] = 10,
+};
+
+static bool rk3188_pmu_power_domain_is_on(enum pmu_power_domain pd)
+{
+       /* 1'b0: power on, 1'b1: power off */
+       return !(readl_relaxed(RK_PMU_VIRT + RK3188_PMU_PWRDN_ST) & BIT(pmu_pd_map[pd]));
+}
+
+static noinline void do_pmu_set_power_domain(enum pmu_power_domain domain, bool on)
+{
+       u8 pd = pmu_pd_map[domain];
+       u32 val = readl_relaxed(RK_PMU_VIRT + RK3188_PMU_PWRDN_CON);
+       if (on)
+               val &= ~BIT(pd);
+       else
+               val |=  BIT(pd);
+       writel_relaxed(val, RK_PMU_VIRT + RK3188_PMU_PWRDN_CON);
+       dsb();
+
+       while ((readl_relaxed(RK_PMU_VIRT + RK3188_PMU_PWRDN_ST) & BIT(pd)) == on)
+               ;
+}
+
+static DEFINE_SPINLOCK(pmu_misc_con1_lock);
+
+static const u8 pmu_req_map[] = {
+       [IDLE_REQ_BUS] = 1,
+       [IDLE_REQ_PERI] = 2,
+       [IDLE_REQ_GPU] = 3,
+       [IDLE_REQ_VIDEO] = 4,
+       [IDLE_REQ_VIO] = 5,
+};
+
+static const u8 pmu_idle_map[] = {
+       [IDLE_REQ_DMA] = 14,
+       [IDLE_REQ_CORE] = 15,
+       [IDLE_REQ_VIO] = 22,
+       [IDLE_REQ_VIDEO] = 23,
+       [IDLE_REQ_GPU] = 24,
+       [IDLE_REQ_PERI] = 25,
+       [IDLE_REQ_BUS] = 26,
+};
+
+static const u8 pmu_ack_map[] = {
+       [IDLE_REQ_DMA] = 17,
+       [IDLE_REQ_CORE] = 18,
+       [IDLE_REQ_VIO] = 27,
+       [IDLE_REQ_VIDEO] = 28,
+       [IDLE_REQ_GPU] = 29,
+       [IDLE_REQ_PERI] = 30,
+       [IDLE_REQ_BUS] = 31,
+};
+
+static int rk3188_pmu_set_idle_request(enum pmu_idle_req req, bool idle)
+{
+       u32 idle_mask = BIT(pmu_idle_map[req]);
+       u32 idle_target = idle << pmu_idle_map[req];
+       u32 ack_mask = BIT(pmu_ack_map[req]);
+       u32 ack_target = idle << pmu_ack_map[req];
+       u32 mask = BIT(pmu_req_map[req]);
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pmu_misc_con1_lock, flags);
+       val = readl_relaxed(RK_PMU_VIRT + RK3188_PMU_MISC_CON1);
+       if (idle)
+               val |=  mask;
+       else
+               val &= ~mask;
+       writel_relaxed(val, RK_PMU_VIRT + RK3188_PMU_MISC_CON1);
+       dsb();
+
+       while ((readl_relaxed(RK_PMU_VIRT + RK3188_PMU_PWRDN_ST) & ack_mask) != ack_target)
+               ;
+       while ((readl_relaxed(RK_PMU_VIRT + RK3188_PMU_PWRDN_ST) & idle_mask) != idle_target)
+               ;
+       spin_unlock_irqrestore(&pmu_misc_con1_lock, flags);
+
+       return 0;
+}
+
+/*
+ *  software should power down or power up power domain one by one. Power down or
+ *  power up multiple power domains simultaneously will result in chip electric current
+ *  change dramatically which will affect the chip function.
+ */
+static DEFINE_SPINLOCK(pmu_pd_lock);
+static u32 lcdc0_qos[CPU_AXI_QOS_NUM_REGS];
+static u32 lcdc1_qos[CPU_AXI_QOS_NUM_REGS];
+static u32 cif0_qos[CPU_AXI_QOS_NUM_REGS];
+static u32 cif1_qos[CPU_AXI_QOS_NUM_REGS];
+static u32 ipp_qos[CPU_AXI_QOS_NUM_REGS];
+static u32 rga_qos[CPU_AXI_QOS_NUM_REGS];
+static u32 gpu_qos[CPU_AXI_QOS_NUM_REGS];
+static u32 vpu_qos[CPU_AXI_QOS_NUM_REGS];
+
+#define SAVE_QOS(array, NAME) CPU_AXI_SAVE_QOS(array, RK3188_CPU_AXI_##NAME##_QOS_BASE)
+#define RESTORE_QOS(array, NAME) CPU_AXI_RESTORE_QOS(array, RK3188_CPU_AXI_##NAME##_QOS_BASE)
+
+static int rk3188_pmu_set_power_domain(enum pmu_power_domain pd, bool on)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&pmu_pd_lock, flags);
+       if (rk3188_pmu_power_domain_is_on(pd) == on) {
+               spin_unlock_irqrestore(&pmu_pd_lock, flags);
+               return 0;
+       }
+       if (!on) {
+               /* if power down, idle request to NIU first */
+               if (pd == PD_VIO) {
+                       SAVE_QOS(lcdc0_qos, LCDC0);
+                       SAVE_QOS(lcdc1_qos, LCDC1);
+                       SAVE_QOS(cif0_qos, CIF0);
+                       SAVE_QOS(cif1_qos, CIF1);
+                       SAVE_QOS(ipp_qos, IPP);
+                       SAVE_QOS(rga_qos, RGA);
+                       rk3188_pmu_set_idle_request(IDLE_REQ_VIO, true);
+               } else if (pd == PD_VIDEO) {
+                       SAVE_QOS(vpu_qos, VPU);
+                       rk3188_pmu_set_idle_request(IDLE_REQ_VIDEO, true);
+               } else if (pd == PD_GPU) {
+                       SAVE_QOS(gpu_qos, GPU);
+                       rk3188_pmu_set_idle_request(IDLE_REQ_GPU, true);
+               }
+       }
+       do_pmu_set_power_domain(pd, on);
+       if (on) {
+               /* if power up, idle request release to NIU */
+               if (pd == PD_VIO) {
+                       rk3188_pmu_set_idle_request(IDLE_REQ_VIO, false);
+                       RESTORE_QOS(lcdc0_qos, LCDC0);
+                       RESTORE_QOS(lcdc1_qos, LCDC1);
+                       RESTORE_QOS(cif0_qos, CIF0);
+                       RESTORE_QOS(cif1_qos, CIF1);
+                       RESTORE_QOS(ipp_qos, IPP);
+                       RESTORE_QOS(rga_qos, RGA);
+               } else if (pd == PD_VIDEO) {
+                       rk3188_pmu_set_idle_request(IDLE_REQ_VIDEO, false);
+                       RESTORE_QOS(vpu_qos, VPU);
+               } else if (pd == PD_GPU) {
+                       rk3188_pmu_set_idle_request(IDLE_REQ_GPU, false);
+                       RESTORE_QOS(gpu_qos, GPU);
+               }
+       }
+       spin_unlock_irqrestore(&pmu_pd_lock, flags);
+
+       return 0;
 }
 
 static void __init rk3188_dt_init_timer(void)
 {
+       rockchip_pmu_ops.set_power_domain = rk3188_pmu_set_power_domain;
+       rockchip_pmu_ops.power_domain_is_on = rk3188_pmu_power_domain_is_on;
+       rockchip_pmu_ops.set_idle_request = rk3188_pmu_set_idle_request;
        of_clk_init(NULL);
        clocksource_of_init();
 }
 
-static const char * const rk3188_dt_compat[] = {
+static const char * const rk3188_dt_compat[] __initconst = {
        "rockchip,rk3188",
        NULL,
 };
 
+static void rk3188_restart(char mode, const char *cmd)
+{
+       u32 boot_flag, boot_mode;
+
+       rockchip_restart_get_boot_mode(cmd, &boot_flag, &boot_mode);
+
+       writel_relaxed(boot_flag, RK_PMU_VIRT + RK3188_PMU_SYS_REG0);   // for loader
+       writel_relaxed(boot_mode, RK_PMU_VIRT + RK3188_PMU_SYS_REG1);   // for linux
+       dsb();
+
+       /* disable remap */
+       writel_relaxed(1 << (12 + 16), RK_GRF_VIRT + RK3188_GRF_SOC_CON0);
+       /* pll enter slow mode */
+       writel_relaxed(RK3188_PLL_MODE_SLOW(RK3188_APLL_ID) |
+                      RK3188_PLL_MODE_SLOW(RK3188_CPLL_ID) |
+                      RK3188_PLL_MODE_SLOW(RK3188_GPLL_ID),
+                      RK_CRU_VIRT + RK3188_CRU_MODE_CON);
+       dsb();
+       writel_relaxed(0xeca8, RK_CRU_VIRT + RK3188_CRU_GLB_SRST_SND);
+       dsb();
+}
+
 DT_MACHINE_START(RK3188_DT, "Rockchip RK3188 (Flattened Device Tree)")
        .smp            = smp_ops(rockchip_smp_ops),
        .map_io         = rk3188_dt_map_io,
        .init_time      = rk3188_dt_init_timer,
        .dt_compat      = rk3188_dt_compat,
+       .restart        = rk3188_restart,
 MACHINE_END
 
 #define CPU 3188
@@ -161,7 +368,7 @@ arch_initcall(rk3188_pie_init);
 #define sram_printascii(s) do {} while (0) /* FIXME */
 #include "ddr_rk30.c"
 
-static int rk3188_ddr_init(void)
+static int __init rk3188_ddr_init(void)
 {
        if (cpu_is_rk3188())
                ddr_init(DDR3_DEFAULT, 300);