tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / cpuidle / sprd_light_sleep.c
1 /*
2  * Lightsleep config for Spreadtrum.
3  *
4  * Copyright (C) 2015 Spreadtrum Ltd.
5  * Author: Icy Zhu <icy.zhu@spreadtrum.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/export.h>
15 #include <linux/sched.h>
16
17 #include <soc/sprd/hardware.h>
18 #include <soc/sprd/sci_glb_regs.h>
19 #include <soc/sprd/sci.h>
20 #include <soc/sprd/cpuidle.h>
21
22 #define INTC_IRQ_EN                     (0x0008)
23 #define INTC_IRQ_DIS        (0x000C)
24
25 /* For MCU_SYS_SLEEP Prepare */
26 static BLOCKING_NOTIFIER_HEAD(sc_cpuidle_chain_head);
27 #if defined(CONFIG_ARCH_SCX35LT8)
28 static int light_sleep = 1;
29 static int trace_debug = 1;
30 #else
31 static int light_sleep = 1;
32 static int trace_debug = 0;
33 #endif
34 static int test_power = 0;
35 static int mcu_sleep_debug = 0;
36
37 /* Enable Lightsleep Function */
38 module_param_named(light_sleep, light_sleep, int, S_IRUGO | S_IWUSR);
39 /* Used to trace32 debug, because the value of trace32 is jumpy-changing while DAP clock be gatenning */
40 module_param_named(trace_debug, trace_debug, int, S_IRUGO | S_IWUSR);
41 /* Used to lightsleep state power test,if test_power be set to 1, the system will keep lightsleep state until to press powerkey */
42 module_param_named(test_power, test_power, int, S_IRUGO | S_IWUSR);
43 /* Used to MCU_SYS_SLEEP debug */
44 module_param_named(mcu_sleep_debug, mcu_sleep_debug, int, S_IRUGO | S_IWUSR);
45
46 int register_sc_cpuidle_notifier(struct notifier_block *nb)
47 {
48         printk("*** %s, nb->notifier_call:%pf ***\n", __func__, nb->notifier_call);
49         return blocking_notifier_chain_register(&sc_cpuidle_chain_head, nb);
50 }
51 EXPORT_SYMBOL_GPL(register_sc_cpuidle_notifier);
52
53 int unregister_sc_cpuidle_notifier(struct notifier_block *nb)
54 {
55         return blocking_notifier_chain_unregister(&sc_cpuidle_chain_head, nb);
56 }
57 EXPORT_SYMBOL_GPL(unregister_sc_cpuidle_notifier);
58
59 int sc_cpuidle_notifier_call_chain(unsigned long val)
60 {
61         int ret = blocking_notifier_call_chain(&sc_cpuidle_chain_head, val, NULL);
62         return notifier_to_errno(ret);
63 }
64 EXPORT_SYMBOL_GPL(sc_cpuidle_notifier_call_chain);
65
66 void light_sleep_en(void)
67 {
68         int error;
69         if (light_sleep) {
70                 if (test_power) {
71                         /* Disable All INTC for test lightsleep power */
72                         __raw_writel(0xffffffef, (void *)(SPRD_INT_BASE + INTC_IRQ_DIS));
73                         __raw_writel(0xffffffff, (void *)(SPRD_INTC0_BASE + INTC_IRQ_DIS));
74                         __raw_writel(0xffffffff, (void *)(SPRD_INTC1_BASE + INTC_IRQ_DIS));
75                         __raw_writel(0xffffffff, (void *)(SPRD_INTC2_BASE + INTC_IRQ_DIS));
76                         __raw_writel(0xffffffff, (void *)(SPRD_INTC3_BASE + INTC_IRQ_DIS));
77                 }
78                 /* Cluster Gating */
79                 sci_glb_set(REG_AP_AHB_APCPU_AUTO_GATE_EN, BIT_APCPU_AUTO_GATE_EN);
80                 /* Light sleep */
81                 sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_LIGHT_SLEEP_EN);
82                 /* Auto Gating */
83                 if (!trace_debug) {
84                         sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_DBG_AUTO_GATE_EN);
85                         sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_TRACE_AUTO_GATE_EN);
86                         /* Disable DAP */
87                         sci_glb_clr(REG_AON_APB_APB_EB0, BIT_APCPU_DAP_EB);
88                 }
89                 /* MCU sys sleep prepare */
90                 if (!(sci_glb_read(REG_AP_AHB_AHB_EB, -1UL) & BIT_DMA_EB)) {
91                         error = sc_cpuidle_notifier_call_chain(SC_CPUIDLE_PREPARE);
92                         if (error) {
93                                 sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN);
94                                 if (mcu_sleep_debug) {
95                                         printk("[CPUIDLE]<mcu_sleep_pre>could not set %s ... \n", __func__);
96                                 }
97                                 return;
98                         }
99                         /* Enable MCU sys sleep */
100                         sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN);
101                 }
102         }
103 }
104 EXPORT_SYMBOL_GPL(light_sleep_en);
105
106 void light_sleep_dis(void)
107 {
108         if (light_sleep) {
109                 /* Enable DAP */
110                 sci_glb_set(REG_AON_APB_APB_EB0, BIT_APCPU_DAP_EB);
111                 sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_DBG_AUTO_GATE_EN);
112                 sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_TRACE_AUTO_GATE_EN);
113                 /* Disable MCU sys sleep */
114                 if (test_power) {
115                         test_power = 0;
116                         sci_glb_set(SPRD_INT_BASE + INTC_IRQ_EN, BIT(14) | BIT(4) | BIT(2));
117                         __raw_writel(0x0010000c, (void *)(SPRD_INTC0_BASE + INTC_IRQ_EN));
118                         __raw_writel(0xf68f4cd0, (void *)(SPRD_INTC1_BASE + INTC_IRQ_EN));
119                         __raw_writel(0x00500000, (void *)(SPRD_INTC2_BASE + INTC_IRQ_EN));
120                         __raw_writel(0x11000000, (void *)(SPRD_INTC3_BASE + INTC_IRQ_EN));
121                 }
122         }
123 }
124 EXPORT_SYMBOL_GPL(light_sleep_dis);
125
126 void __init light_sleep_init(void)
127 {
128         /* Cluster Gating */
129         sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_CORE_AUTO_GATE_EN);
130         /* AP_AHB clock Auto gating while ap doesn't into mcu_sys_sleep */
131         sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_AP_AHB_AUTO_GATE_EN);
132         /* For wakeup lightsleep */
133         sci_glb_set(REG_PMU_APB_AP_WAKEUP_POR_CFG, BIT_AP_WAKEUP_POR_N);
134         return;
135 }
136 arch_initcall(light_sleep_init);