blob: 07d05227d5289ffe8ddf4a0cf0be3e1fe86c591f [file] [log] [blame]
/*
* 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);
}
}