zte's code,first commit
Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297510/pcu.c b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297510/pcu.c
new file mode 100644
index 0000000..07d0522
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297510/pcu.c
@@ -0,0 +1,331 @@
+/*
+ * linux/arch/arm/mach-zx297510/pcu.c
+ *
+ * Copyright (C) 2013 ZTE-TSP
+ *
+ * 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.
+ */
+ #include <linux/errno.h>
+ #include <linux/irq.h>
+ #include <linux/module.h>
+
+ #include <asm/io.h>
+
+ #include <mach/iomap.h>
+ #include <mach/zx-pm.h>
+ #include <mach/zx297510-pm.h>
+ #include <mach/pcu.h>
+ #include <mach/spinlock.h>
+
+/*
+ * return external intterrupt number from ex8-ex15,
+ * return value is 8-15
+ */
+unsigned int pcu_int_vector_8in1(void)
+{
+ void __iomem *reg_base = EX_INT_VECTOR_REG;
+ unsigned int vector_8in1 = 0;
+
+ vector_8in1 = ioread32(reg_base);
+
+ return ((vector_8in1&0x7) + 8);
+}
+EXPORT_SYMBOL(pcu_int_vector_8in1);
+
+
+
+/*external int 8-15 need extra clear*/
+ void pcu_int_clear_8in1(PCU_INT_INDEX index)
+ {
+ void __iomem *reg_base = EX_INT_TOP_CLEAR_REG;
+ unsigned int vector=0;
+
+ if ( (index >= PCU_EX8_INT)&&(index <= PCU_EX15_INT) ){
+ /*
+ *in 7510 platform, 8in1 interrupt would be used by different cores.
+ *when any core installs a new 8in1 interrupt, another core may be
+ * responding another 8in1 interrupt, so 8in1 interrupt shouldn't be
+ *cleared. in this case, nothing to be done. but a new problem comes,
+ * the core install new 8in1 interrupt will receive a fake interrupt.
+ */
+ vector=pcu_int_vector_8in1();
+ if (index != (vector + PCU_EX0_INT) )
+ return;
+
+ reg_spin_lock();
+ iowrite32(0x1,reg_base);
+ reg_spin_unlock();
+ }
+ }
+EXPORT_SYMBOL(pcu_int_clear_8in1);
+
+/*only pulse int need be clear*/
+ void pcu_int_clear(PCU_INT_INDEX index)
+ {
+ unsigned int reg_num = index&0x100;
+ unsigned int bit_mask = 1<<(index&0x1f);
+ void __iomem *reg_base = NULL;
+ unsigned int tmp;
+
+ if (reg_num == 0x100)
+ reg_base = PCU_BASE_VA + 0xec;
+ else
+ reg_base = PCU_BASE_VA + 0x80;
+
+ reg_spin_lock();
+ tmp = ioread32(reg_base);
+ tmp |= bit_mask;
+ iowrite32(tmp,reg_base);
+ reg_spin_unlock();
+
+ pcu_int_clear_8in1(index);
+ }
+EXPORT_SYMBOL(pcu_int_clear);
+
+
+void pcu_int_set_type(PCU_INT_INDEX index,unsigned int type)
+{
+ unsigned int reg_num = index&0x100;
+ unsigned int bit_index = index&0x1f;
+ unsigned int bit_mask = 1<<bit_index;
+
+ void __iomem *level_regbase = NULL;
+ void __iomem *pol_regbase = NULL;
+ unsigned int tmp;
+ unsigned int level;
+ unsigned int pol;
+
+ if (reg_num == 0x100){
+ level_regbase = PCU_BASE_VA + 0xe4;
+ pol_regbase = PCU_BASE_VA + 0xe8;
+ }
+ else{
+ level_regbase = PCU_BASE_VA + 0x60;
+ pol_regbase = PCU_BASE_VA + 0x70;
+ }
+
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ level = 1;
+ pol = 1;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ level = 0;
+ pol = 1;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ level = 1;
+ pol= 0;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ level = 0;
+ pol = 0;
+ break;
+ default:
+ BUG();
+ }
+
+ reg_spin_lock();
+ /* set level*/
+ tmp = ioread32(level_regbase);
+ tmp &= ~bit_mask;
+ tmp |= level<<bit_index;
+ iowrite32(tmp,level_regbase);
+
+ /* set polarity */
+ tmp = ioread32(pol_regbase);
+ tmp &= ~bit_mask;
+ tmp |= pol<<bit_index;
+ iowrite32(tmp,pol_regbase);
+ reg_spin_unlock();
+
+}
+EXPORT_SYMBOL(pcu_int_set_type);
+
+
+
+/* low power function */
+
+extern unsigned int wake_source_int_for_suspend[];
+extern unsigned int wake_source_int_for_dpidle[];
+/**
+ * enable wake source for cpu
+ *
+ */
+static void pcu_enable_wake_source(unsigned int *wake_source)
+{
+ unsigned int int1, int2, int_timer;
+
+ int1 = wake_source[0];
+ int2 = wake_source[1];
+ int_timer = wake_source[2];
+
+ reg_spin_lock();
+
+ pm_clr_reg(AP_INT_WAKE_DIS_REG1, int1);
+ pm_clr_reg(AP_INT_WAKE_DIS_REG2, int2);
+ pm_clr_reg(TIMER_INT_WAKE_DIS_REG, int_timer);
+
+ reg_spin_unlock();
+}
+
+static void pcu_set_wake_source(cpu_sleep_type_t sleep_type)
+{
+ if(CPU_SLEEP_TYPE_LP1 == sleep_type)
+ {
+ pcu_enable_wake_source(wake_source_int_for_suspend);
+ }
+ else if(CPU_SLEEP_TYPE_IDLE_LP2 == sleep_type || CPU_SLEEP_TYPE_LP3 == sleep_type)
+ {
+ pcu_enable_wake_source(wake_source_int_for_dpidle);
+ }
+}
+
+/**
+ * init pcu when pm initial.
+ *
+ */
+void zx297510_pcu_init(void)
+{
+ pr_info("[SLP] Power/PCU_INIT \n");
+
+ pm_clr_reg(ARM_AP_CONFIG_REG, PCU_MODE_MASK);
+ pm_set_pll_used();
+ pm_set_reg(ARM_AP_SLEEP_TIME_REG, PCU_AP_SLEEP_TIME_DIS);
+
+ pm_write_reg(AP_L2_POWER_ENABLE_REG, 0);
+
+ pm_write_reg(AP_INT_WAKE_DIS_REG1, 0xFFFFFFFF);
+ pm_write_reg(AP_INT_WAKE_DIS_REG2, 0xFFFFFFFF);
+
+ pm_set_reg(AP_LOW_POWER_REG1, 0); /* bypass disable */
+ pm_set_reg(AP_LOW_POWER_REG2, 0); /* 1 - enter DS 0 - exit DS */
+}
+
+/**
+ * set&enable PCU for interrupt/clock/powerdomain/pll/iram.
+ *
+ */
+int zx297510_set_pcu(void)
+{
+ cpu_sleep_type_t sleep_type;
+ u32 sleep_time;
+
+ sleep_type = pm_get_sleep_type();
+ sleep_time = pm_get_sleep_time();
+
+ pm_set_pll_used();
+
+ if(CPU_SLEEP_TYPE_LP1 == sleep_type) /* shutdown */
+ {
+ pm_set_reg(ARM_AP_CONFIG_REG, PCU_SHUTDOWN_MODE);
+ pm_set_reg(ARM_AP_CONFIG_REG, PCU_MG_CLK_RESET|PCU_L2_CLK_GATE|PCU_PLL_OFF);
+
+ pm_write_reg(ARM_AP_SLEEP_TIME_REG, 0xffffffff);
+
+ pm_write_reg(AP_L2_POWER_ENABLE_REG, 1);
+
+ }
+ else if(CPU_SLEEP_TYPE_IDLE_LP2 == sleep_type) /* dormant */
+ {
+ pm_set_reg(ARM_AP_CONFIG_REG, PCU_DORMANT_MODE);
+ pm_set_reg(ARM_AP_CONFIG_REG, PCU_MG_CLK_RESET|PCU_L2_CLK_GATE|PCU_PLL_OFF);
+
+ pm_write_reg(ARM_AP_SLEEP_TIME_REG, sleep_time);
+ }
+ else if(CPU_SLEEP_TYPE_LP3 == sleep_type)
+ {
+ pm_set_reg(ARM_AP_CONFIG_REG, PCU_STANDBY_MODE);
+ pm_clr_reg(ARM_AP_CONFIG_REG, PCU_PLL_OFF);
+ pm_set_reg(ARM_AP_CONFIG_REG, PCU_MG_CLK_RESET|PCU_L2_CLK_GATE);
+
+ pm_write_reg(ARM_AP_SLEEP_TIME_REG, sleep_time);
+ }
+ else
+ WARN_ON(1);
+
+ pcu_set_wake_source(sleep_type);
+
+ return 0;
+}
+
+/**
+ * clear&disable PCU for interrupt/clock/powerdomain/pll/iram.
+ *
+ */
+int zx297510_clear_pcu(void)
+{
+ reg_spin_lock();
+
+ pm_clr_reg(ARM_AP_CONFIG_REG, PCU_MODE_MASK);
+
+ pm_write_reg(AP_L2_POWER_ENABLE_REG, 0);
+
+ pm_write_reg(AP_INT_WAKE_DIS_REG1, 0xFFFFFFFF);
+ pm_write_reg(AP_INT_WAKE_DIS_REG2, 0xFFFFFFFF);
+
+ pm_set_reg(TIMER_INT_WAKE_DIS_REG, WAKE_SRC_TIMER3);
+
+ reg_spin_unlock();
+
+ return 0;
+}
+
+/**
+ * when sleep/dormant/poweroff, pll will be closed.
+ *
+ */
+void pcu_set_pll_used(unsigned pll)
+{
+ if(PCU_USE_PLL_MAIN == pll)
+ pm_clr_reg(ARM_AP_CONFIG_REG, pll);
+ else
+ pm_set_reg(ARM_AP_CONFIG_REG, pll);
+}
+
+/**
+ * get wakeup setting.
+ *
+ */
+void pcu_get_wakeup_setting(unsigned int *pointer)
+{
+ pointer[0] = pm_read_reg(AP_INT_WAKE_DIS_REG1);
+ pointer[1] = pm_read_reg(AP_INT_WAKE_DIS_REG2);
+ pointer[2] = pm_read_reg(TIMER_INT_WAKE_DIS_REG);
+}
+
+void pcu_clr_timer3_int(void)
+{
+ reg_spin_lock();
+
+ pm_set_reg(TIMER_INT_DDR_SW_CLEAR_REG, WAKE_SRC_TIMER3);
+
+ reg_spin_unlock();
+}
+
+/**
+ * set wakeup enable.
+ *
+ * now only support ext_int.
+ */
+void pcu_irq_wakeup(PCU_INT_INDEX index, int enable)
+{
+ if ( (index < PCU_EX0_INT) || (index > PCU_EX15_INT) )
+ return;
+
+ if(enable)
+ {
+ wake_source_int_for_suspend[0] |= (1<<index);
+ wake_source_int_for_dpidle[0] |= (1<<index);
+ }
+ else
+ {
+ wake_source_int_for_suspend[0] &= ~(1<<index);
+ wake_source_int_for_dpidle[0] &= ~(1<<index);
+ }
+}
+