2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2009-05-16 11:51:14 +01:00
/*
* linux / arch / arm / kernel / smp_scu . c
*
* Copyright ( C ) 2002 ARM Ltd .
* All Rights Reserved
*/
# include <linux/init.h>
# include <linux/io.h>
2012-02-24 12:20:16 +01:00
# include <asm/smp_plat.h>
2009-05-16 11:51:14 +01:00
# include <asm/smp_scu.h>
2009-05-18 16:26:27 +01:00
# include <asm/cacheflush.h>
2011-09-15 11:45:15 +01:00
# include <asm/cputype.h>
2009-05-16 11:51:14 +01:00
# define SCU_CTRL 0x00
2014-07-31 02:07:00 +01:00
# define SCU_ENABLE (1 << 0)
2014-07-31 02:07:37 +01:00
# define SCU_STANDBY_ENABLE (1 << 5)
2009-05-16 11:51:14 +01:00
# define SCU_CONFIG 0x04
# define SCU_CPU_STATUS 0x08
2017-09-17 18:45:20 +02:00
# define SCU_CPU_STATUS_MASK GENMASK(1, 0)
2009-05-16 11:51:14 +01:00
# define SCU_INVALIDATE 0x0c
# define SCU_FPGA_REVISION 0x10
2011-06-13 15:28:53 +01:00
# ifdef CONFIG_SMP
2009-05-16 11:51:14 +01:00
/*
* Get the number of CPU cores from the SCU configuration
*/
unsigned int __init scu_get_core_count ( void __iomem * scu_base )
{
2013-02-07 11:14:21 +00:00
unsigned int ncores = readl_relaxed ( scu_base + SCU_CONFIG ) ;
2009-05-16 11:51:14 +01:00
return ( ncores & 0x03 ) + 1 ;
}
/*
* Enable the SCU
*/
2011-09-25 08:25:43 +01:00
void scu_enable ( void __iomem * scu_base )
2009-05-16 11:51:14 +01:00
{
u32 scu_ctrl ;
2011-09-15 11:45:15 +01:00
# ifdef CONFIG_ARM_ERRATA_764369
/* Cortex-A9 only */
2013-01-30 17:38:21 +01:00
if ( ( read_cpuid_id ( ) & 0xff0ffff0 ) = = 0x410fc090 ) {
2013-02-07 11:14:21 +00:00
scu_ctrl = readl_relaxed ( scu_base + 0x30 ) ;
2011-09-15 11:45:15 +01:00
if ( ! ( scu_ctrl & 1 ) )
2013-02-07 11:14:21 +00:00
writel_relaxed ( scu_ctrl | 0x1 , scu_base + 0x30 ) ;
2011-09-15 11:45:15 +01:00
}
# endif
2013-02-07 11:14:21 +00:00
scu_ctrl = readl_relaxed ( scu_base + SCU_CTRL ) ;
2009-11-04 12:16:38 +00:00
/* already enabled? */
2014-07-31 02:07:00 +01:00
if ( scu_ctrl & SCU_ENABLE )
2009-11-04 12:16:38 +00:00
return ;
2014-07-31 02:07:00 +01:00
scu_ctrl | = SCU_ENABLE ;
2014-07-31 02:07:37 +01:00
/* Cortex-A9 earlier than r2p0 has no standby bit in SCU */
if ( ( read_cpuid_id ( ) & 0xff0ffff0 ) = = 0x410fc090 & &
( read_cpuid_id ( ) & 0x00f0000f ) > = 0x00200000 )
scu_ctrl | = SCU_STANDBY_ENABLE ;
2013-02-07 11:14:21 +00:00
writel_relaxed ( scu_ctrl , scu_base + SCU_CTRL ) ;
2009-05-18 16:26:27 +01:00
/*
* Ensure that the data accessed by CPU0 before the SCU was
* initialised is visible to the other CPUs .
*/
flush_cache_all ( ) ;
2009-05-16 11:51:14 +01:00
}
2011-06-13 15:28:53 +01:00
# endif
2011-02-04 10:36:39 +00:00
2017-09-17 18:45:19 +02:00
static int scu_set_power_mode_internal ( void __iomem * scu_base ,
unsigned int logical_cpu ,
unsigned int mode )
2011-02-04 10:36:39 +00:00
{
unsigned int val ;
2017-09-17 18:45:19 +02:00
int cpu = MPIDR_AFFINITY_LEVEL ( cpu_logical_map ( logical_cpu ) , 0 ) ;
2011-02-04 10:36:39 +00:00
if ( mode > 3 | | mode = = 1 | | cpu > 3 )
return - EINVAL ;
2017-09-17 18:45:20 +02:00
val = readb_relaxed ( scu_base + SCU_CPU_STATUS + cpu ) ;
val & = ~ SCU_CPU_STATUS_MASK ;
2011-02-04 10:36:39 +00:00
val | = mode ;
2013-02-07 11:14:21 +00:00
writeb_relaxed ( val , scu_base + SCU_CPU_STATUS + cpu ) ;
2011-02-04 10:36:39 +00:00
return 0 ;
}
2017-09-17 18:45:19 +02:00
/*
* Set the executing CPUs power mode as defined . This will be in
* preparation for it executing a WFI instruction .
*
* This function must be called with preemption disabled , and as it
* has the side effect of disabling coherency , caches must have been
* flushed . Interrupts must also have been disabled .
*/
int scu_power_mode ( void __iomem * scu_base , unsigned int mode )
{
return scu_set_power_mode_internal ( scu_base , smp_processor_id ( ) , mode ) ;
}
/*
* Set the given ( logical ) CPU ' s power mode to SCU_PM_NORMAL .
*/
int scu_cpu_power_enable ( void __iomem * scu_base , unsigned int cpu )
{
return scu_set_power_mode_internal ( scu_base , cpu , SCU_PM_NORMAL ) ;
}
2017-09-17 18:45:20 +02:00
int scu_get_cpu_power_mode ( void __iomem * scu_base , unsigned int logical_cpu )
{
unsigned int val ;
int cpu = MPIDR_AFFINITY_LEVEL ( cpu_logical_map ( logical_cpu ) , 0 ) ;
if ( cpu > 3 )
return - EINVAL ;
val = readb_relaxed ( scu_base + SCU_CPU_STATUS + cpu ) ;
val & = SCU_CPU_STATUS_MASK ;
return val ;
}