2012-01-25 14:43:28 -07:00
/*
2013-02-28 21:32:10 +00:00
* Copyright ( C ) 2012 , 2013 NVIDIA CORPORATION . All rights reserved .
2012-01-25 14:43:28 -07:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
# include <linux/kernel.h>
2013-04-03 19:31:28 +08:00
# include <linux/clk.h>
2012-01-25 14:43:28 -07:00
# include <linux/io.h>
# include <linux/of.h>
2013-02-28 21:32:10 +00:00
# include <linux/of_address.h>
2013-10-16 19:19:00 +02:00
# include <linux/tegra-powergate.h>
2012-01-25 14:43:28 -07:00
2013-07-03 17:50:41 +08:00
# include "flowctrl.h"
2013-04-03 19:31:47 +08:00
# include "fuse.h"
# include "pm.h"
2013-04-03 19:31:46 +08:00
# include "pmc.h"
2013-04-03 19:31:47 +08:00
# include "sleep.h"
2013-08-12 17:40:01 +08:00
# define TEGRA_POWER_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */
# define TEGRA_POWER_SYSCLK_OE (1 << 11) /* system clock enable */
2013-04-03 19:31:47 +08:00
# define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */
# define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */
# define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
2013-04-03 19:31:46 +08:00
2013-02-28 21:32:11 +00:00
# define PMC_CTRL 0x0
# define PMC_CTRL_INTR_LOW (1 << 17)
# define PMC_PWRGATE_TOGGLE 0x30
# define PMC_PWRGATE_TOGGLE_START (1 << 8)
# define PMC_REMOVE_CLAMPING 0x34
# define PMC_PWRGATE_STATUS 0x38
2013-04-03 19:31:28 +08:00
# define PMC_CPUPWRGOOD_TIMER 0xc8
# define PMC_CPUPWROFF_TIMER 0xcc
2013-02-28 21:32:11 +00:00
static u8 tegra_cpu_domains [ ] = {
0xFF , /* not available for CPU0 */
TEGRA_POWERGATE_CPU1 ,
TEGRA_POWERGATE_CPU2 ,
TEGRA_POWERGATE_CPU3 ,
} ;
static DEFINE_SPINLOCK ( tegra_powergate_lock ) ;
2012-01-25 14:43:28 -07:00
2013-02-28 21:32:10 +00:00
static void __iomem * tegra_pmc_base ;
static bool tegra_pmc_invert_interrupt ;
2013-04-03 19:31:28 +08:00
static struct clk * tegra_pclk ;
2013-02-28 21:32:10 +00:00
2013-04-03 19:31:46 +08:00
struct pmc_pm_data {
u32 cpu_good_time ; /* CPU power good time in uS */
u32 cpu_off_time ; /* CPU power off time in uS */
u32 core_osc_time ; /* Core power good osc time in uS */
u32 core_pmu_time ; /* Core power good pmu time in uS */
u32 core_off_time ; /* Core power off time in uS */
bool corereq_high ; /* Core power request active-high */
bool sysclkreq_high ; /* System clock request active-high */
bool combined_req ; /* Combined pwr req for CPU & Core */
bool cpu_pwr_good_en ; /* CPU power good signal is enabled */
u32 lp0_vec_phy_addr ; /* The phy addr of LP0 warm boot code */
u32 lp0_vec_size ; /* The size of LP0 warm boot code */
enum tegra_suspend_mode suspend_mode ;
} ;
static struct pmc_pm_data pmc_pm_data ;
2012-01-25 14:43:28 -07:00
static inline u32 tegra_pmc_readl ( u32 reg )
{
2013-02-28 21:32:10 +00:00
return readl ( tegra_pmc_base + reg ) ;
2012-01-25 14:43:28 -07:00
}
static inline void tegra_pmc_writel ( u32 val , u32 reg )
{
2013-02-28 21:32:10 +00:00
writel ( val , tegra_pmc_base + reg ) ;
2012-01-25 14:43:28 -07:00
}
2013-02-28 21:32:11 +00:00
static int tegra_pmc_get_cpu_powerdomain_id ( int cpuid )
{
if ( cpuid < = 0 | | cpuid > = num_possible_cpus ( ) )
return - EINVAL ;
return tegra_cpu_domains [ cpuid ] ;
}
static bool tegra_pmc_powergate_is_powered ( int id )
{
return ( tegra_pmc_readl ( PMC_PWRGATE_STATUS ) > > id ) & 1 ;
}
static int tegra_pmc_powergate_set ( int id , bool new_state )
{
bool old_state ;
unsigned long flags ;
spin_lock_irqsave ( & tegra_powergate_lock , flags ) ;
old_state = tegra_pmc_powergate_is_powered ( id ) ;
WARN_ON ( old_state = = new_state ) ;
tegra_pmc_writel ( PMC_PWRGATE_TOGGLE_START | id , PMC_PWRGATE_TOGGLE ) ;
spin_unlock_irqrestore ( & tegra_powergate_lock , flags ) ;
return 0 ;
}
static int tegra_pmc_powergate_remove_clamping ( int id )
{
u32 mask ;
/*
* Tegra has a bug where PCIE and VDE clamping masks are
* swapped relatively to the partition ids .
*/
if ( id = = TEGRA_POWERGATE_VDEC )
mask = ( 1 < < TEGRA_POWERGATE_PCIE ) ;
else if ( id = = TEGRA_POWERGATE_PCIE )
mask = ( 1 < < TEGRA_POWERGATE_VDEC ) ;
else
mask = ( 1 < < id ) ;
tegra_pmc_writel ( mask , PMC_REMOVE_CLAMPING ) ;
return 0 ;
}
bool tegra_pmc_cpu_is_powered ( int cpuid )
{
int id ;
id = tegra_pmc_get_cpu_powerdomain_id ( cpuid ) ;
if ( id < 0 )
return false ;
return tegra_pmc_powergate_is_powered ( id ) ;
}
int tegra_pmc_cpu_power_on ( int cpuid )
{
int id ;
id = tegra_pmc_get_cpu_powerdomain_id ( cpuid ) ;
if ( id < 0 )
return id ;
return tegra_pmc_powergate_set ( id , true ) ;
}
int tegra_pmc_cpu_remove_clamping ( int cpuid )
{
int id ;
id = tegra_pmc_get_cpu_powerdomain_id ( cpuid ) ;
if ( id < 0 )
return id ;
return tegra_pmc_powergate_remove_clamping ( id ) ;
}
2013-08-20 15:47:38 -06:00
void tegra_pmc_restart ( enum reboot_mode mode , const char * cmd )
{
u32 val ;
val = tegra_pmc_readl ( 0 ) ;
val | = 0x10 ;
tegra_pmc_writel ( val , 0 ) ;
}
2013-04-03 19:31:28 +08:00
# ifdef CONFIG_PM_SLEEP
2013-04-03 19:31:47 +08:00
static void set_power_timers ( u32 us_on , u32 us_off , unsigned long rate )
2013-04-03 19:31:28 +08:00
{
unsigned long long ticks ;
unsigned long long pclk ;
static unsigned long tegra_last_pclk ;
if ( WARN_ON_ONCE ( rate < = 0 ) )
pclk = 100000000 ;
else
pclk = rate ;
if ( ( rate ! = tegra_last_pclk ) ) {
ticks = ( us_on * pclk ) + 999999ull ;
do_div ( ticks , 1000000 ) ;
tegra_pmc_writel ( ( unsigned long ) ticks , PMC_CPUPWRGOOD_TIMER ) ;
ticks = ( us_off * pclk ) + 999999ull ;
do_div ( ticks , 1000000 ) ;
tegra_pmc_writel ( ( unsigned long ) ticks , PMC_CPUPWROFF_TIMER ) ;
wmb ( ) ;
}
tegra_last_pclk = pclk ;
}
2013-04-03 19:31:47 +08:00
enum tegra_suspend_mode tegra_pmc_get_suspend_mode ( void )
{
return pmc_pm_data . suspend_mode ;
}
2013-08-12 17:40:03 +08:00
void tegra_pmc_set_suspend_mode ( enum tegra_suspend_mode mode )
{
if ( mode < TEGRA_SUSPEND_NONE | | mode > = TEGRA_MAX_SUSPEND_MODE )
return ;
pmc_pm_data . suspend_mode = mode ;
}
void tegra_pmc_suspend ( void )
{
tegra_pmc_writel ( virt_to_phys ( tegra_resume ) , PMC_SCRATCH41 ) ;
}
void tegra_pmc_resume ( void )
{
tegra_pmc_writel ( 0x0 , PMC_SCRATCH41 ) ;
}
2013-04-03 19:31:47 +08:00
void tegra_pmc_pm_set ( enum tegra_suspend_mode mode )
{
2013-07-03 17:50:41 +08:00
u32 reg , csr_reg ;
2013-04-03 19:31:47 +08:00
unsigned long rate = 0 ;
reg = tegra_pmc_readl ( PMC_CTRL ) ;
reg | = TEGRA_POWER_CPU_PWRREQ_OE ;
reg & = ~ TEGRA_POWER_EFFECT_LP0 ;
2013-07-03 17:50:41 +08:00
switch ( tegra_chip_id ) {
case TEGRA20 :
case TEGRA30 :
break ;
default :
/* Turn off CRAIL */
csr_reg = flowctrl_read_cpu_csr ( 0 ) ;
csr_reg & = ~ FLOW_CTRL_CSR_ENABLE_EXT_MASK ;
csr_reg | = FLOW_CTRL_CSR_ENABLE_EXT_CRAIL ;
flowctrl_write_cpu_csr ( 0 , csr_reg ) ;
break ;
}
2013-04-03 19:31:47 +08:00
switch ( mode ) {
2013-08-12 17:40:03 +08:00
case TEGRA_SUSPEND_LP1 :
rate = 32768 ;
break ;
2013-04-03 19:31:47 +08:00
case TEGRA_SUSPEND_LP2 :
rate = clk_get_rate ( tegra_pclk ) ;
break ;
default :
break ;
}
set_power_timers ( pmc_pm_data . cpu_good_time , pmc_pm_data . cpu_off_time ,
rate ) ;
tegra_pmc_writel ( reg , PMC_CTRL ) ;
}
void tegra_pmc_suspend_init ( void )
{
u32 reg ;
/* Always enable CPU power request */
reg = tegra_pmc_readl ( PMC_CTRL ) ;
reg | = TEGRA_POWER_CPU_PWRREQ_OE ;
tegra_pmc_writel ( reg , PMC_CTRL ) ;
2013-08-12 17:40:01 +08:00
reg = tegra_pmc_readl ( PMC_CTRL ) ;
if ( ! pmc_pm_data . sysclkreq_high )
reg | = TEGRA_POWER_SYSCLK_POLARITY ;
else
reg & = ~ TEGRA_POWER_SYSCLK_POLARITY ;
/* configure the output polarity while the request is tristated */
tegra_pmc_writel ( reg , PMC_CTRL ) ;
/* now enable the request */
reg | = TEGRA_POWER_SYSCLK_OE ;
tegra_pmc_writel ( reg , PMC_CTRL ) ;
2013-04-03 19:31:47 +08:00
}
2013-04-03 19:31:28 +08:00
# endif
2012-01-25 14:43:28 -07:00
static const struct of_device_id matches [ ] __initconst = {
2013-10-08 12:50:04 +08:00
{ . compatible = " nvidia,tegra124-pmc " } ,
2013-02-26 16:27:42 +00:00
{ . compatible = " nvidia,tegra114-pmc " } ,
{ . compatible = " nvidia,tegra30-pmc " } ,
2012-01-25 14:43:28 -07:00
{ . compatible = " nvidia,tegra20-pmc " } ,
{ }
} ;
ARM: tegra: split tegra_pmc_init() in two
Tegra's board file currently initializes clocks much earlier than those
for most other ARM SoCs. The reason is:
* The PMC HW block is involved in the path of some interrupts (i.e. it
inverts, or not, the IRQ input pin dedicated to the PMIC).
* So, that part of the PMC must be initialized early so that the IRQ
polarity is correct.
* The PMC initialization is currently monolithic, and the PMC has some
clock inputs, so the init routine ends up calling of_clk_get_by_name(),
and hence clocks must be set up early too.
In order to defer clock initialization to the more typical location,
split out the portions of tegra_pmc_init() that are truly IRQ-related
into a separate tegra_pmc_init_irq(), which can be called from the
machine descriptor's .init_irq() function, and defer the rest until
the machine descriptor's .init_machine() function. This allows the
clock initiliazation to happen from the machine descriptor's
.init_time() function, as is typical.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
2013-08-20 15:17:35 -06:00
void __init tegra_pmc_init_irq ( void )
2012-01-25 14:43:28 -07:00
{
2013-02-28 21:32:10 +00:00
struct device_node * np ;
ARM: tegra: split tegra_pmc_init() in two
Tegra's board file currently initializes clocks much earlier than those
for most other ARM SoCs. The reason is:
* The PMC HW block is involved in the path of some interrupts (i.e. it
inverts, or not, the IRQ input pin dedicated to the PMIC).
* So, that part of the PMC must be initialized early so that the IRQ
polarity is correct.
* The PMC initialization is currently monolithic, and the PMC has some
clock inputs, so the init routine ends up calling of_clk_get_by_name(),
and hence clocks must be set up early too.
In order to defer clock initialization to the more typical location,
split out the portions of tegra_pmc_init() that are truly IRQ-related
into a separate tegra_pmc_init_irq(), which can be called from the
machine descriptor's .init_irq() function, and defer the rest until
the machine descriptor's .init_machine() function. This allows the
clock initiliazation to happen from the machine descriptor's
.init_time() function, as is typical.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
2013-08-20 15:17:35 -06:00
u32 val ;
2013-02-28 21:32:10 +00:00
np = of_find_matching_node ( NULL , matches ) ;
BUG_ON ( ! np ) ;
2012-01-25 14:43:28 -07:00
2013-02-28 21:32:10 +00:00
tegra_pmc_base = of_iomap ( np , 0 ) ;
2012-01-25 14:43:28 -07:00
2013-02-28 21:32:10 +00:00
tegra_pmc_invert_interrupt = of_property_read_bool ( np ,
" nvidia,invert-interrupt " ) ;
ARM: tegra: split tegra_pmc_init() in two
Tegra's board file currently initializes clocks much earlier than those
for most other ARM SoCs. The reason is:
* The PMC HW block is involved in the path of some interrupts (i.e. it
inverts, or not, the IRQ input pin dedicated to the PMIC).
* So, that part of the PMC must be initialized early so that the IRQ
polarity is correct.
* The PMC initialization is currently monolithic, and the PMC has some
clock inputs, so the init routine ends up calling of_clk_get_by_name(),
and hence clocks must be set up early too.
In order to defer clock initialization to the more typical location,
split out the portions of tegra_pmc_init() that are truly IRQ-related
into a separate tegra_pmc_init_irq(), which can be called from the
machine descriptor's .init_irq() function, and defer the rest until
the machine descriptor's .init_machine() function. This allows the
clock initiliazation to happen from the machine descriptor's
.init_time() function, as is typical.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
2013-08-20 15:17:35 -06:00
val = tegra_pmc_readl ( PMC_CTRL ) ;
if ( tegra_pmc_invert_interrupt )
val | = PMC_CTRL_INTR_LOW ;
else
val & = ~ PMC_CTRL_INTR_LOW ;
tegra_pmc_writel ( val , PMC_CTRL ) ;
}
void __init tegra_pmc_init ( void )
{
struct device_node * np ;
u32 prop ;
enum tegra_suspend_mode suspend_mode ;
u32 core_good_time [ 2 ] = { 0 , 0 } ;
u32 lp0_vec [ 2 ] = { 0 , 0 } ;
np = of_find_matching_node ( NULL , matches ) ;
BUG_ON ( ! np ) ;
2013-04-03 19:31:28 +08:00
tegra_pclk = of_clk_get_by_name ( np , " pclk " ) ;
WARN_ON ( IS_ERR ( tegra_pclk ) ) ;
2013-04-03 19:31:46 +08:00
/* Grabbing the power management configurations */
if ( of_property_read_u32 ( np , " nvidia,suspend-mode " , & prop ) ) {
suspend_mode = TEGRA_SUSPEND_NONE ;
} else {
switch ( prop ) {
case 0 :
suspend_mode = TEGRA_SUSPEND_LP0 ;
break ;
case 1 :
suspend_mode = TEGRA_SUSPEND_LP1 ;
break ;
case 2 :
suspend_mode = TEGRA_SUSPEND_LP2 ;
break ;
default :
suspend_mode = TEGRA_SUSPEND_NONE ;
break ;
}
}
2013-04-03 19:31:47 +08:00
suspend_mode = tegra_pm_validate_suspend_mode ( suspend_mode ) ;
2013-04-03 19:31:46 +08:00
if ( of_property_read_u32 ( np , " nvidia,cpu-pwr-good-time " , & prop ) )
suspend_mode = TEGRA_SUSPEND_NONE ;
pmc_pm_data . cpu_good_time = prop ;
if ( of_property_read_u32 ( np , " nvidia,cpu-pwr-off-time " , & prop ) )
suspend_mode = TEGRA_SUSPEND_NONE ;
pmc_pm_data . cpu_off_time = prop ;
if ( of_property_read_u32_array ( np , " nvidia,core-pwr-good-time " ,
core_good_time , ARRAY_SIZE ( core_good_time ) ) )
suspend_mode = TEGRA_SUSPEND_NONE ;
pmc_pm_data . core_osc_time = core_good_time [ 0 ] ;
pmc_pm_data . core_pmu_time = core_good_time [ 1 ] ;
if ( of_property_read_u32 ( np , " nvidia,core-pwr-off-time " ,
& prop ) )
suspend_mode = TEGRA_SUSPEND_NONE ;
pmc_pm_data . core_off_time = prop ;
pmc_pm_data . corereq_high = of_property_read_bool ( np ,
" nvidia,core-power-req-active-high " ) ;
pmc_pm_data . sysclkreq_high = of_property_read_bool ( np ,
" nvidia,sys-clock-req-active-high " ) ;
pmc_pm_data . combined_req = of_property_read_bool ( np ,
" nvidia,combined-power-req " ) ;
pmc_pm_data . cpu_pwr_good_en = of_property_read_bool ( np ,
" nvidia,cpu-pwr-good-en " ) ;
if ( of_property_read_u32_array ( np , " nvidia,lp0-vec " , lp0_vec ,
ARRAY_SIZE ( lp0_vec ) ) )
if ( suspend_mode = = TEGRA_SUSPEND_LP0 )
suspend_mode = TEGRA_SUSPEND_LP1 ;
pmc_pm_data . lp0_vec_phy_addr = lp0_vec [ 0 ] ;
pmc_pm_data . lp0_vec_size = lp0_vec [ 1 ] ;
pmc_pm_data . suspend_mode = suspend_mode ;
2013-02-28 21:32:10 +00:00
}