2019-05-31 01:09:57 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2012-02-10 01:47:44 +02:00
/*
2017-03-28 13:42:54 +01:00
* drivers / soc / tegra / flowctrl . c
2012-02-10 01:47:44 +02:00
*
2017-03-28 13:42:54 +01:00
* Functions and macros to control the flowcontroller
2012-02-10 01:47:44 +02:00
*
* Copyright ( c ) 2010 - 2012 , NVIDIA Corporation . All rights reserved .
*/
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>
2017-03-28 13:42:55 +01:00
# include <linux/platform_device.h>
2012-02-10 01:47:44 +02:00
2017-03-28 13:42:54 +01:00
# include <soc/tegra/common.h>
# include <soc/tegra/flowctrl.h>
2014-07-11 09:52:41 +02:00
# include <soc/tegra/fuse.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 )
{
2017-03-28 13:42:55 +01:00
if ( WARN_ONCE ( IS_ERR_OR_NULL ( tegra_flowctrl_base ) ,
2017-03-28 13:42:54 +01:00
" Tegra flowctrl not initialised! \n " ) )
return ;
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 ] ;
2017-03-28 13:42:55 +01:00
if ( WARN_ONCE ( IS_ERR_OR_NULL ( tegra_flowctrl_base ) ,
2017-03-28 13:42:54 +01:00
" Tegra flowctrl not initialised! \n " ) )
return 0 ;
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
2017-03-28 13:42:55 +01:00
static int tegra_flowctrl_probe ( struct platform_device * pdev )
{
void __iomem * base = tegra_flowctrl_base ;
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
tegra_flowctrl_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( tegra_flowctrl_base ) )
2017-04-11 05:40:41 +02:00
return PTR_ERR ( tegra_flowctrl_base ) ;
2017-03-28 13:42:55 +01:00
iounmap ( base ) ;
return 0 ;
}
static const struct of_device_id tegra_flowctrl_match [ ] = {
2017-03-28 13:42:58 +01:00
{ . compatible = " nvidia,tegra210-flowctrl " } ,
2014-08-26 08:14:04 +02:00
{ . compatible = " nvidia,tegra124-flowctrl " } ,
{ . compatible = " nvidia,tegra114-flowctrl " } ,
{ . compatible = " nvidia,tegra30-flowctrl " } ,
{ . compatible = " nvidia,tegra20-flowctrl " } ,
{ }
} ;
2017-03-28 13:42:55 +01:00
static struct platform_driver tegra_flowctrl_driver = {
. driver = {
. name = " tegra-flowctrl " ,
. suppress_bind_attrs = true ,
. of_match_table = tegra_flowctrl_match ,
} ,
. probe = tegra_flowctrl_probe ,
} ;
builtin_platform_driver ( tegra_flowctrl_driver ) ;
2017-03-28 13:42:54 +01:00
static int __init tegra_flowctrl_init ( void )
2014-08-26 08:14:04 +02:00
{
2017-03-28 13:42:58 +01:00
struct resource res ;
2014-08-26 08:14:04 +02:00
struct device_node * np ;
2017-03-28 13:42:54 +01:00
if ( ! soc_is_tegra ( ) )
return 0 ;
2017-03-28 13:42:55 +01:00
np = of_find_matching_node ( NULL , tegra_flowctrl_match ) ;
2014-08-26 08:14:04 +02:00
if ( np ) {
2017-03-28 13:42:58 +01:00
if ( of_address_to_resource ( np , 0 , & res ) < 0 ) {
pr_err ( " failed to get flowctrl register \n " ) ;
return - ENXIO ;
2014-08-26 08:14:04 +02:00
}
of_node_put ( np ) ;
2017-03-28 13:42:58 +01:00
} else if ( IS_ENABLED ( CONFIG_ARM ) ) {
/*
* Hardcoded fallback for 32 - bit Tegra
* devices if device tree node is missing .
*/
res . start = 0x60007000 ;
res . end = 0x60007fff ;
res . flags = IORESOURCE_MEM ;
} else {
/*
* At this point we ' re running on a Tegra ,
* that doesn ' t support the flow controller
* ( eg . Tegra186 ) , so just return .
*/
return 0 ;
2014-08-26 08:14:04 +02:00
}
2017-03-28 13:42:58 +01:00
tegra_flowctrl_base = ioremap_nocache ( res . start , resource_size ( & res ) ) ;
2017-03-28 13:42:54 +01:00
if ( ! tegra_flowctrl_base )
return - ENXIO ;
return 0 ;
2014-08-26 08:14:04 +02:00
}
2017-03-28 13:42:54 +01:00
early_initcall ( tegra_flowctrl_init ) ;