2012-02-10 01:47:44 +02:00
/*
* arch / arm / mach - tegra / flowctrl . c
*
* functions and macros to control the flowcontroller
*
* Copyright ( c ) 2010 - 2012 , NVIDIA Corporation . All rights reserved .
*
* 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 that 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/>.
*/
2014-07-11 09:44:49 +02:00
# include <linux/cpumask.h>
2012-02-10 01:47:44 +02:00
# include <linux/init.h>
# include <linux/io.h>
2014-07-11 09:44:49 +02:00
# include <linux/kernel.h>
2014-08-26 08:14:04 +02:00
# include <linux/of.h>
# include <linux/of_address.h>
2012-02-10 01:47:44 +02:00
2014-07-11 09:52:41 +02:00
# include <soc/tegra/fuse.h>
2012-02-10 01:47:44 +02:00
# include "flowctrl.h"
2013-01-03 08:27:05 +02:00
static u8 flowctrl_offset_halt_cpu [ ] = {
2012-02-10 01:47:44 +02:00
FLOW_CTRL_HALT_CPU0_EVENTS ,
FLOW_CTRL_HALT_CPU1_EVENTS ,
FLOW_CTRL_HALT_CPU1_EVENTS + 8 ,
FLOW_CTRL_HALT_CPU1_EVENTS + 16 ,
} ;
2013-01-03 08:27:05 +02:00
static u8 flowctrl_offset_cpu_csr [ ] = {
2012-02-10 01:47:44 +02:00
FLOW_CTRL_CPU0_CSR ,
FLOW_CTRL_CPU1_CSR ,
FLOW_CTRL_CPU1_CSR + 8 ,
FLOW_CTRL_CPU1_CSR + 16 ,
} ;
2014-08-26 08:14:04 +02:00
static void __iomem * tegra_flowctrl_base ;
2012-02-10 01:47:44 +02:00
static void flowctrl_update ( u8 offset , u32 value )
{
2014-08-26 08:14:04 +02:00
writel ( value , tegra_flowctrl_base + offset ) ;
2012-02-10 01:47:44 +02:00
/* ensure the update has reached the flow controller */
wmb ( ) ;
2014-08-26 08:14:04 +02:00
readl_relaxed ( tegra_flowctrl_base + offset ) ;
2012-02-10 01:47:44 +02:00
}
2012-10-31 17:41:20 +08:00
u32 flowctrl_read_cpu_csr ( unsigned int cpuid )
{
u8 offset = flowctrl_offset_cpu_csr [ cpuid ] ;
2014-08-26 08:14:04 +02:00
return readl ( tegra_flowctrl_base + offset ) ;
2012-10-31 17:41:20 +08:00
}
2012-02-10 01:47:44 +02:00
void flowctrl_write_cpu_csr ( unsigned int cpuid , u32 value )
{
2012-05-14 13:27:09 +03:00
return flowctrl_update ( flowctrl_offset_cpu_csr [ cpuid ] , value ) ;
2012-02-10 01:47:44 +02:00
}
void flowctrl_write_cpu_halt ( unsigned int cpuid , u32 value )
{
2012-05-14 13:27:09 +03:00
return flowctrl_update ( flowctrl_offset_halt_cpu [ cpuid ] , value ) ;
2012-02-10 01:47:44 +02:00
}
2012-10-31 17:41:20 +08:00
void flowctrl_cpu_suspend_enter ( unsigned int cpuid )
{
unsigned int reg ;
int i ;
reg = flowctrl_read_cpu_csr ( cpuid ) ;
2014-07-11 09:52:41 +02:00
switch ( tegra_get_chip_id ( ) ) {
2013-01-15 22:11:01 +00:00
case TEGRA20 :
/* clear wfe bitmap */
reg & = ~ TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP ;
/* clear wfi bitmap */
reg & = ~ TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP ;
/* pwr gating on wfe */
reg | = TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 < < cpuid ;
break ;
case TEGRA30 :
2013-07-03 17:50:43 +08:00
case TEGRA114 :
2013-10-11 17:58:38 +08:00
case TEGRA124 :
2013-01-15 22:11:01 +00:00
/* clear wfe bitmap */
reg & = ~ TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP ;
/* clear wfi bitmap */
reg & = ~ TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP ;
/* pwr gating on wfi */
reg | = TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 < < cpuid ;
break ;
}
2012-10-31 17:41:20 +08:00
reg | = FLOW_CTRL_CSR_INTR_FLAG ; /* clear intr flag */
reg | = FLOW_CTRL_CSR_EVENT_FLAG ; /* clear event flag */
reg | = FLOW_CTRL_CSR_ENABLE ; /* pwr gating */
flowctrl_write_cpu_csr ( cpuid , reg ) ;
for ( i = 0 ; i < num_possible_cpus ( ) ; i + + ) {
if ( i = = cpuid )
continue ;
reg = flowctrl_read_cpu_csr ( i ) ;
reg | = FLOW_CTRL_CSR_EVENT_FLAG ;
reg | = FLOW_CTRL_CSR_INTR_FLAG ;
flowctrl_write_cpu_csr ( i , reg ) ;
}
}
void flowctrl_cpu_suspend_exit ( unsigned int cpuid )
{
unsigned int reg ;
/* Disable powergating via flow controller for CPU0 */
reg = flowctrl_read_cpu_csr ( cpuid ) ;
2014-07-11 09:52:41 +02:00
switch ( tegra_get_chip_id ( ) ) {
2013-01-15 22:11:01 +00:00
case TEGRA20 :
/* clear wfe bitmap */
reg & = ~ TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP ;
/* clear wfi bitmap */
reg & = ~ TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP ;
break ;
case TEGRA30 :
2013-07-03 17:50:43 +08:00
case TEGRA114 :
2013-10-11 17:58:38 +08:00
case TEGRA124 :
2013-01-15 22:11:01 +00:00
/* clear wfe bitmap */
reg & = ~ TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP ;
/* clear wfi bitmap */
reg & = ~ TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP ;
break ;
}
2012-10-31 17:41:20 +08:00
reg & = ~ FLOW_CTRL_CSR_ENABLE ; /* clear enable */
reg | = FLOW_CTRL_CSR_INTR_FLAG ; /* clear intr */
reg | = FLOW_CTRL_CSR_EVENT_FLAG ; /* clear event */
flowctrl_write_cpu_csr ( cpuid , reg ) ;
}
2014-08-26 08:14:04 +02:00
static const struct of_device_id matches [ ] __initconst = {
{ . compatible = " nvidia,tegra124-flowctrl " } ,
{ . compatible = " nvidia,tegra114-flowctrl " } ,
{ . compatible = " nvidia,tegra30-flowctrl " } ,
{ . compatible = " nvidia,tegra20-flowctrl " } ,
{ }
} ;
void __init tegra_flowctrl_init ( void )
{
/* hardcoded fallback if device tree node is missing */
unsigned long base = 0x60007000 ;
unsigned long size = SZ_4K ;
struct device_node * np ;
np = of_find_matching_node ( NULL , matches ) ;
if ( np ) {
struct resource res ;
if ( of_address_to_resource ( np , 0 , & res ) = = 0 ) {
size = resource_size ( & res ) ;
base = res . start ;
}
of_node_put ( np ) ;
}
tegra_flowctrl_base = ioremap_nocache ( base , size ) ;
}