diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/cpuidle/sprd_light_sleep.c | |
Diffstat (limited to 'drivers/cpuidle/sprd_light_sleep.c')
| -rw-r--r-- | drivers/cpuidle/sprd_light_sleep.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/drivers/cpuidle/sprd_light_sleep.c b/drivers/cpuidle/sprd_light_sleep.c new file mode 100644 index 00000000..f649eec1 --- /dev/null +++ b/drivers/cpuidle/sprd_light_sleep.c @@ -0,0 +1,136 @@ +/* + * Lightsleep config for Spreadtrum. + * + * Copyright (C) 2015 Spreadtrum Ltd. + * Author: Icy Zhu <icy.zhu@spreadtrum.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/sched.h> + +#include <soc/sprd/hardware.h> +#include <soc/sprd/sci_glb_regs.h> +#include <soc/sprd/sci.h> +#include <soc/sprd/cpuidle.h> + +#define INTC_IRQ_EN (0x0008) +#define INTC_IRQ_DIS (0x000C) + +/* For MCU_SYS_SLEEP Prepare */ +static BLOCKING_NOTIFIER_HEAD(sc_cpuidle_chain_head); +#if defined(CONFIG_ARCH_SCX35LT8) +static int light_sleep = 1; +static int trace_debug = 1; +#else +static int light_sleep = 1; +static int trace_debug = 0; +#endif +static int test_power = 0; +static int mcu_sleep_debug = 0; + +/* Enable Lightsleep Function */ +module_param_named(light_sleep, light_sleep, int, S_IRUGO | S_IWUSR); +/* Used to trace32 debug, because the value of trace32 is jumpy-changing while DAP clock be gatenning */ +module_param_named(trace_debug, trace_debug, int, S_IRUGO | S_IWUSR); +/* Used to lightsleep state power test,if test_power be set to 1, the system will keep lightsleep state until to press powerkey */ +module_param_named(test_power, test_power, int, S_IRUGO | S_IWUSR); +/* Used to MCU_SYS_SLEEP debug */ +module_param_named(mcu_sleep_debug, mcu_sleep_debug, int, S_IRUGO | S_IWUSR); + +int register_sc_cpuidle_notifier(struct notifier_block *nb) +{ + printk("*** %s, nb->notifier_call:%pf ***\n", __func__, nb->notifier_call); + return blocking_notifier_chain_register(&sc_cpuidle_chain_head, nb); +} +EXPORT_SYMBOL_GPL(register_sc_cpuidle_notifier); + +int unregister_sc_cpuidle_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&sc_cpuidle_chain_head, nb); +} +EXPORT_SYMBOL_GPL(unregister_sc_cpuidle_notifier); + +int sc_cpuidle_notifier_call_chain(unsigned long val) +{ + int ret = blocking_notifier_call_chain(&sc_cpuidle_chain_head, val, NULL); + return notifier_to_errno(ret); +} +EXPORT_SYMBOL_GPL(sc_cpuidle_notifier_call_chain); + +void light_sleep_en(void) +{ + int error; + if (light_sleep) { + if (test_power) { + /* Disable All INTC for test lightsleep power */ + __raw_writel(0xffffffef, (void *)(SPRD_INT_BASE + INTC_IRQ_DIS)); + __raw_writel(0xffffffff, (void *)(SPRD_INTC0_BASE + INTC_IRQ_DIS)); + __raw_writel(0xffffffff, (void *)(SPRD_INTC1_BASE + INTC_IRQ_DIS)); + __raw_writel(0xffffffff, (void *)(SPRD_INTC2_BASE + INTC_IRQ_DIS)); + __raw_writel(0xffffffff, (void *)(SPRD_INTC3_BASE + INTC_IRQ_DIS)); + } + /* Cluster Gating */ + sci_glb_set(REG_AP_AHB_APCPU_AUTO_GATE_EN, BIT_APCPU_AUTO_GATE_EN); + /* Light sleep */ + sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_LIGHT_SLEEP_EN); + /* Auto Gating */ + if (!trace_debug) { + sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_DBG_AUTO_GATE_EN); + sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_TRACE_AUTO_GATE_EN); + /* Disable DAP */ + sci_glb_clr(REG_AON_APB_APB_EB0, BIT_APCPU_DAP_EB); + } + /* MCU sys sleep prepare */ + if (!(sci_glb_read(REG_AP_AHB_AHB_EB, -1UL) & BIT_DMA_EB)) { + error = sc_cpuidle_notifier_call_chain(SC_CPUIDLE_PREPARE); + if (error) { + sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN); + if (mcu_sleep_debug) { + printk("[CPUIDLE]<mcu_sleep_pre>could not set %s ... \n", __func__); + } + return; + } + /* Enable MCU sys sleep */ + sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN); + } + } +} +EXPORT_SYMBOL_GPL(light_sleep_en); + +void light_sleep_dis(void) +{ + if (light_sleep) { + /* Enable DAP */ + sci_glb_set(REG_AON_APB_APB_EB0, BIT_APCPU_DAP_EB); + sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_DBG_AUTO_GATE_EN); + sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_TRACE_AUTO_GATE_EN); + /* Disable MCU sys sleep */ + if (test_power) { + test_power = 0; + sci_glb_set(SPRD_INT_BASE + INTC_IRQ_EN, BIT(14) | BIT(4) | BIT(2)); + __raw_writel(0x0010000c, (void *)(SPRD_INTC0_BASE + INTC_IRQ_EN)); + __raw_writel(0xf68f4cd0, (void *)(SPRD_INTC1_BASE + INTC_IRQ_EN)); + __raw_writel(0x00500000, (void *)(SPRD_INTC2_BASE + INTC_IRQ_EN)); + __raw_writel(0x11000000, (void *)(SPRD_INTC3_BASE + INTC_IRQ_EN)); + } + } +} +EXPORT_SYMBOL_GPL(light_sleep_dis); + +void __init light_sleep_init(void) +{ + /* Cluster Gating */ + sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_APCPU_CORE_AUTO_GATE_EN); + /* AP_AHB clock Auto gating while ap doesn't into mcu_sys_sleep */ + sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_AP_AHB_AUTO_GATE_EN); + /* For wakeup lightsleep */ + sci_glb_set(REG_PMU_APB_AP_WAKEUP_POR_CFG, BIT_AP_WAKEUP_POR_N); + return; +} +arch_initcall(light_sleep_init); |
