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>
# include <linux/io.h>
# include <linux/of.h>
2013-02-28 21:32:10 +00:00
# include <linux/of_address.h>
2012-01-25 14:43:28 -07: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
# define TEGRA_POWERGATE_PCIE 3
# define TEGRA_POWERGATE_VDEC 4
# define TEGRA_POWERGATE_CPU1 9
# define TEGRA_POWERGATE_CPU2 10
# define TEGRA_POWERGATE_CPU3 11
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 ;
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 ) ;
}
2012-01-25 14:43:28 -07:00
static const struct of_device_id matches [ ] __initconst = {
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 " } ,
{ }
} ;
2013-02-28 21:32:10 +00:00
static void tegra_pmc_parse_dt ( void )
2012-01-25 14:43:28 -07:00
{
2013-02-28 21:32:10 +00:00
struct device_node * np ;
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 " ) ;
}
void __init tegra_pmc_init ( void )
{
u32 val ;
2012-01-25 14:43:28 -07:00
2013-02-28 21:32:10 +00:00
tegra_pmc_parse_dt ( ) ;
2012-01-25 14:43:28 -07:00
val = tegra_pmc_readl ( PMC_CTRL ) ;
2013-02-28 21:32:10 +00:00
if ( tegra_pmc_invert_interrupt )
2012-01-25 14:43:28 -07:00
val | = PMC_CTRL_INTR_LOW ;
else
val & = ~ PMC_CTRL_INTR_LOW ;
tegra_pmc_writel ( val , PMC_CTRL ) ;
}