2017-12-25 20:54:31 +01:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
// http://www.samsung.com
//
// Cloned from linux/arch/arm/mach-vexpress/platsmp.c
//
// Copyright (C) 2002 ARM Ltd.
// All Rights Reserved
2010-07-26 21:08:52 +09:00
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <linux/jiffies.h>
# include <linux/smp.h>
# include <linux/io.h>
2014-05-13 07:13:44 +09:00
# include <linux/of_address.h>
2015-12-18 09:02:11 +05:30
# include <linux/soc/samsung/exynos-regs-pmu.h>
2010-07-26 21:08:52 +09:00
# include <asm/cacheflush.h>
2014-09-14 02:49:31 +09:00
# include <asm/cp15.h>
2012-01-20 12:01:12 +01:00
# include <asm/smp_plat.h>
2010-07-26 21:08:52 +09:00
# include <asm/smp_scu.h>
2012-12-11 13:58:43 +09:00
# include <asm/firmware.h>
2010-07-26 21:08:52 +09:00
2014-07-19 03:43:22 +09:00
# include <mach/map.h>
2011-09-08 13:15:22 +01:00
# include "common.h"
2011-02-14 16:33:10 +09:00
extern void exynos4_secondary_startup ( void ) ;
2010-07-26 21:08:52 +09:00
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
/* XXX exynos_pen_release is cargo culted code - DO NOT COPY XXX */
volatile int exynos_pen_release = - 1 ;
2014-09-14 02:49:31 +09:00
# ifdef CONFIG_HOTPLUG_CPU
2014-09-14 02:49:32 +09:00
static inline void cpu_leave_lowpower ( u32 core_id )
2014-09-14 02:49:31 +09:00
{
unsigned int v ;
asm volatile (
" mrc p15, 0, %0, c1, c0, 0 \n "
" orr %0, %0, %1 \n "
" mcr p15, 0, %0, c1, c0, 0 \n "
" mrc p15, 0, %0, c1, c0, 1 \n "
" orr %0, %0, %2 \n "
" mcr p15, 0, %0, c1, c0, 1 \n "
: " =&r " ( v )
: " Ir " ( CR_C ) , " Ir " ( 0x40 )
: " cc " ) ;
}
static inline void platform_do_lowpower ( unsigned int cpu , int * spurious )
{
u32 mpidr = cpu_logical_map ( cpu ) ;
u32 core_id = MPIDR_AFFINITY_LEVEL ( mpidr , 0 ) ;
for ( ; ; ) {
/* Turn the CPU off on next WFI instruction. */
exynos_cpu_power_down ( core_id ) ;
wfi ( ) ;
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
if ( exynos_pen_release = = core_id ) {
2014-09-14 02:49:31 +09:00
/*
* OK , proper wakeup , we ' re done
*/
break ;
}
/*
* Getting here , means that we have come out of WFI without
* having been woken up - this shouldn ' t happen
*
* Just note it happening - when we ' re woken , we can report
* its occurrence .
*/
( * spurious ) + + ;
}
}
# endif /* CONFIG_HOTPLUG_CPU */
2014-07-19 04:45:02 +09:00
/**
* exynos_core_power_down : power down the specified cpu
* @ cpu : the cpu to power down
*
* Power down the specified cpu . The sequence must be finished by a
* call to cpu_do_idle ( )
*
*/
void exynos_cpu_power_down ( int cpu )
{
2015-03-27 02:32:56 +09:00
u32 core_conf ;
2015-02-27 05:50:41 +09:00
if ( cpu = = 0 & & ( soc_is_exynos5420 ( ) | | soc_is_exynos5800 ( ) ) ) {
2014-11-07 09:20:16 +09:00
/*
* Bypass power down for CPU0 during suspend . Check for
* the SYS_PWR_REG value to decide if we are suspending
* the system .
*/
int val = pmu_raw_readl ( EXYNOS5_ARM_CORE0_SYS_PWR_REG ) ;
if ( ! ( val & S5P_CORE_LOCAL_PWR_EN ) )
return ;
}
2015-03-27 02:32:56 +09:00
core_conf = pmu_raw_readl ( EXYNOS_ARM_CORE_CONFIGURATION ( cpu ) ) ;
core_conf & = ~ S5P_CORE_LOCAL_PWR_EN ;
pmu_raw_writel ( core_conf , EXYNOS_ARM_CORE_CONFIGURATION ( cpu ) ) ;
2014-07-19 04:45:02 +09:00
}
/**
* exynos_cpu_power_up : power up the specified cpu
* @ cpu : the cpu to power up
*
* Power up the specified cpu
*/
void exynos_cpu_power_up ( int cpu )
{
2015-03-27 02:32:56 +09:00
u32 core_conf = S5P_CORE_LOCAL_PWR_EN ;
if ( soc_is_exynos3250 ( ) )
core_conf | = S5P_CORE_AUTOWAKEUP_EN ;
pmu_raw_writel ( core_conf ,
2014-07-26 17:54:21 +02:00
EXYNOS_ARM_CORE_CONFIGURATION ( cpu ) ) ;
2014-07-19 04:45:02 +09:00
}
/**
* exynos_cpu_power_state : returns the power state of the cpu
* @ cpu : the cpu to retrieve the power state from
*
*/
int exynos_cpu_power_state ( int cpu )
{
2014-07-26 17:54:21 +02:00
return ( pmu_raw_readl ( EXYNOS_ARM_CORE_STATUS ( cpu ) ) &
2014-07-19 04:45:02 +09:00
S5P_CORE_LOCAL_PWR_EN ) ;
}
/**
* exynos_cluster_power_down : power down the specified cluster
* @ cluster : the cluster to power down
*/
void exynos_cluster_power_down ( int cluster )
{
2014-07-26 17:54:21 +02:00
pmu_raw_writel ( 0 , EXYNOS_COMMON_CONFIGURATION ( cluster ) ) ;
2014-07-19 04:45:02 +09:00
}
/**
* exynos_cluster_power_up : power up the specified cluster
* @ cluster : the cluster to power up
*/
void exynos_cluster_power_up ( int cluster )
{
2014-07-26 17:54:21 +02:00
pmu_raw_writel ( S5P_CORE_LOCAL_PWR_EN ,
EXYNOS_COMMON_CONFIGURATION ( cluster ) ) ;
2014-07-19 04:45:02 +09:00
}
/**
* exynos_cluster_power_state : returns the power state of the cluster
* @ cluster : the cluster to retrieve the power state from
*
*/
int exynos_cluster_power_state ( int cluster )
{
2014-07-26 17:54:21 +02:00
return ( pmu_raw_readl ( EXYNOS_COMMON_STATUS ( cluster ) ) &
S5P_CORE_LOCAL_PWR_EN ) ;
2014-07-19 04:45:02 +09:00
}
2018-05-10 13:02:54 +02:00
/**
* exynos_scu_enable : enables SCU for Cortex - A9 based system
*/
void exynos_scu_enable ( void )
{
struct device_node * np ;
static void __iomem * scu_base ;
if ( ! scu_base ) {
np = of_find_compatible_node ( NULL , NULL , " arm,cortex-a9-scu " ) ;
if ( np ) {
scu_base = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
} else {
scu_base = ioremap ( scu_a9_get_base ( ) , SZ_4K ) ;
}
}
scu_enable ( scu_base ) ;
}
2015-03-18 14:09:57 +01:00
static void __iomem * cpu_boot_reg_base ( void )
2012-11-24 11:13:48 +09:00
{
if ( soc_is_exynos4210 ( ) & & samsung_rev ( ) = = EXYNOS4210_REV_1_1 )
2014-07-19 03:43:22 +09:00
return pmu_base_addr + S5P_INFORM5 ;
2014-05-13 07:13:44 +09:00
return sysram_base_addr ;
2012-11-24 11:13:48 +09:00
}
static inline void __iomem * cpu_boot_reg ( int cpu )
{
void __iomem * boot_reg ;
boot_reg = cpu_boot_reg_base ( ) ;
2014-05-13 07:13:44 +09:00
if ( ! boot_reg )
2015-07-10 19:20:58 +09:00
return IOMEM_ERR_PTR ( - ENODEV ) ;
2012-11-24 11:13:48 +09:00
if ( soc_is_exynos4412 ( ) )
boot_reg + = 4 * cpu ;
2014-05-26 04:16:11 +09:00
else if ( soc_is_exynos5420 ( ) | | soc_is_exynos5800 ( ) )
2013-06-19 00:29:35 +09:00
boot_reg + = 4 ;
2012-11-24 11:13:48 +09:00
return boot_reg ;
}
2011-07-16 13:39:09 +09:00
2014-09-25 18:15:13 +09:00
/*
* Set wake up by local power mode and execute software reset for given core .
*
* Currently this is needed only when booting secondary CPU on Exynos3250 .
*/
2015-03-18 14:09:57 +01:00
void exynos_core_restart ( u32 core_id )
2014-09-25 18:15:13 +09:00
{
2019-03-26 15:03:59 +01:00
unsigned int timeout = 16 ;
2014-09-25 18:15:13 +09:00
u32 val ;
if ( ! of_machine_is_compatible ( " samsung,exynos3250 " ) )
return ;
2019-03-26 15:03:59 +01:00
while ( timeout & & ! pmu_raw_readl ( S5P_PMU_SPARE2 ) ) {
timeout - - ;
2015-03-27 02:32:56 +09:00
udelay ( 10 ) ;
2019-03-26 15:03:59 +01:00
}
if ( timeout = = 0 ) {
pr_err ( " cpu core %u restart failed \n " , core_id ) ;
return ;
}
2015-03-27 02:32:56 +09:00
udelay ( 10 ) ;
2014-09-25 18:15:13 +09:00
val = pmu_raw_readl ( EXYNOS_ARM_CORE_STATUS ( core_id ) ) ;
val | = S5P_CORE_WAKEUP_FROM_LOCAL_CFG ;
pmu_raw_writel ( val , EXYNOS_ARM_CORE_STATUS ( core_id ) ) ;
pmu_raw_writel ( EXYNOS_CORE_PO_RESET ( core_id ) , EXYNOS_SWRESET ) ;
}
ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has
been offlined can online itself before being requested, which results
in things going astray on the next online/offline cycle.
What happens in the normal online/offline/online cycle is:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads -1
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
However, as the write of -1 of pen_release is not fully flushed back to
memory, and the checking of pen_release is done with caches disabled,
this allows CPU3 the opportunity to read the old value of pen_release:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads 3
starts boot
pen_release = -1
requests boot of CPU3
pen_release = 3
flush cache line
Fix this by grouping the write of pen_release along with its cache line
flushing code to ensure that any update to pen_release is always pushed
out to physical memory.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2010-12-18 10:53:12 +00:00
/*
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
* XXX CARGO CULTED CODE - DO NOT COPY XXX
*
* Write exynos_pen_release in a way that is guaranteed to be visible to
* all observers , irrespective of whether they ' re taking part in coherency
ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has
been offlined can online itself before being requested, which results
in things going astray on the next online/offline cycle.
What happens in the normal online/offline/online cycle is:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads -1
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
However, as the write of -1 of pen_release is not fully flushed back to
memory, and the checking of pen_release is done with caches disabled,
this allows CPU3 the opportunity to read the old value of pen_release:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads 3
starts boot
pen_release = -1
requests boot of CPU3
pen_release = 3
flush cache line
Fix this by grouping the write of pen_release along with its cache line
flushing code to ensure that any update to pen_release is always pushed
out to physical memory.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2010-12-18 10:53:12 +00:00
* or not . This is necessary for the hotplug code to work reliably .
*/
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
static void exynos_write_pen_release ( int val )
ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has
been offlined can online itself before being requested, which results
in things going astray on the next online/offline cycle.
What happens in the normal online/offline/online cycle is:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads -1
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
However, as the write of -1 of pen_release is not fully flushed back to
memory, and the checking of pen_release is done with caches disabled,
this allows CPU3 the opportunity to read the old value of pen_release:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads 3
starts boot
pen_release = -1
requests boot of CPU3
pen_release = 3
flush cache line
Fix this by grouping the write of pen_release along with its cache line
flushing code to ensure that any update to pen_release is always pushed
out to physical memory.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2010-12-18 10:53:12 +00:00
{
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
exynos_pen_release = val ;
ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has
been offlined can online itself before being requested, which results
in things going astray on the next online/offline cycle.
What happens in the normal online/offline/online cycle is:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads -1
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
However, as the write of -1 of pen_release is not fully flushed back to
memory, and the checking of pen_release is done with caches disabled,
this allows CPU3 the opportunity to read the old value of pen_release:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads 3
starts boot
pen_release = -1
requests boot of CPU3
pen_release = 3
flush cache line
Fix this by grouping the write of pen_release along with its cache line
flushing code to ensure that any update to pen_release is always pushed
out to physical memory.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2010-12-18 10:53:12 +00:00
smp_wmb ( ) ;
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
sync_cache_w ( & exynos_pen_release ) ;
ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has
been offlined can online itself before being requested, which results
in things going astray on the next online/offline cycle.
What happens in the normal online/offline/online cycle is:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads -1
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
However, as the write of -1 of pen_release is not fully flushed back to
memory, and the checking of pen_release is done with caches disabled,
this allows CPU3 the opportunity to read the old value of pen_release:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads 3
starts boot
pen_release = -1
requests boot of CPU3
pen_release = 3
flush cache line
Fix this by grouping the write of pen_release along with its cache line
flushing code to ensure that any update to pen_release is always pushed
out to physical memory.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2010-12-18 10:53:12 +00:00
}
2010-07-26 21:08:52 +09:00
static DEFINE_SPINLOCK ( boot_lock ) ;
2013-06-17 15:43:14 -04:00
static void exynos_secondary_init ( unsigned int cpu )
2010-07-26 21:08:52 +09:00
{
/*
* let the primary processor know we ' re out of the
* pen , then head off into the C entry point
*/
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
exynos_write_pen_release ( - 1 ) ;
2010-07-26 21:08:52 +09:00
/*
* Synchronise with the boot thread .
*/
spin_lock ( & boot_lock ) ;
spin_unlock ( & boot_lock ) ;
}
2015-03-18 14:09:57 +01:00
int exynos_set_boot_addr ( u32 core_id , unsigned long boot_addr )
2015-03-18 14:09:55 +01:00
{
int ret ;
/*
* Try to set boot address using firmware first
* and fall back to boot register if it fails .
*/
ret = call_firmware_op ( set_cpu_boot_addr , core_id , boot_addr ) ;
if ( ret & & ret ! = - ENOSYS )
goto fail ;
if ( ret = = - ENOSYS ) {
void __iomem * boot_reg = cpu_boot_reg ( core_id ) ;
if ( IS_ERR ( boot_reg ) ) {
ret = PTR_ERR ( boot_reg ) ;
goto fail ;
}
2016-06-21 11:20:24 +01:00
writel_relaxed ( boot_addr , boot_reg ) ;
2015-03-18 14:09:55 +01:00
ret = 0 ;
}
fail :
return ret ;
}
2015-03-18 14:09:57 +01:00
int exynos_get_boot_addr ( u32 core_id , unsigned long * boot_addr )
2015-03-18 14:09:56 +01:00
{
int ret ;
/*
* Try to get boot address using firmware first
* and fall back to boot register if it fails .
*/
ret = call_firmware_op ( get_cpu_boot_addr , core_id , boot_addr ) ;
if ( ret & & ret ! = - ENOSYS )
goto fail ;
if ( ret = = - ENOSYS ) {
void __iomem * boot_reg = cpu_boot_reg ( core_id ) ;
if ( IS_ERR ( boot_reg ) ) {
ret = PTR_ERR ( boot_reg ) ;
goto fail ;
}
2016-06-21 11:20:24 +01:00
* boot_addr = readl_relaxed ( boot_reg ) ;
2015-03-18 14:09:56 +01:00
ret = 0 ;
}
fail :
return ret ;
}
2013-06-17 15:43:14 -04:00
static int exynos_boot_secondary ( unsigned int cpu , struct task_struct * idle )
2010-07-26 21:08:52 +09:00
{
unsigned long timeout ;
2014-07-16 02:59:18 +09:00
u32 mpidr = cpu_logical_map ( cpu ) ;
u32 core_id = MPIDR_AFFINITY_LEVEL ( mpidr , 0 ) ;
2014-05-13 07:13:44 +09:00
int ret = - ENOSYS ;
2010-07-26 21:08:52 +09:00
/*
* Set synchronisation state between this boot processor
* and the secondary one
*/
spin_lock ( & boot_lock ) ;
/*
* The secondary processor is waiting to be released from
* the holding pen - release it , then wait for it to flag
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
* that it has been released by resetting exynos_pen_release .
2010-07-26 21:08:52 +09:00
*
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
* Note that " exynos_pen_release " is the hardware CPU core ID , whereas
2010-07-26 21:08:52 +09:00
* " cpu " is Linux ' s internal ID .
*/
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
exynos_write_pen_release ( core_id ) ;
2010-07-26 21:08:52 +09:00
2014-07-16 02:59:18 +09:00
if ( ! exynos_cpu_power_state ( core_id ) ) {
exynos_cpu_power_up ( core_id ) ;
2011-07-16 13:39:09 +09:00
timeout = 10 ;
/* wait max 10 ms until cpu1 is on */
2014-07-16 02:59:18 +09:00
while ( exynos_cpu_power_state ( core_id )
! = S5P_CORE_LOCAL_PWR_EN ) {
2019-01-28 23:06:45 +00:00
if ( timeout = = 0 )
2011-07-16 13:39:09 +09:00
break ;
2019-01-28 23:06:45 +00:00
timeout - - ;
2011-07-16 13:39:09 +09:00
mdelay ( 1 ) ;
}
if ( timeout = = 0 ) {
printk ( KERN_ERR " cpu1 power enable failed " ) ;
spin_unlock ( & boot_lock ) ;
return - ETIMEDOUT ;
}
}
2014-09-25 18:15:13 +09:00
exynos_core_restart ( core_id ) ;
2010-07-26 21:08:52 +09:00
/*
* Send the secondary CPU a soft interrupt , thereby causing
* the boot monitor to read the system wide flags register ,
* and branch to the address found there .
*/
timeout = jiffies + ( 1 * HZ ) ;
while ( time_before ( jiffies , timeout ) ) {
2012-12-11 13:58:43 +09:00
unsigned long boot_addr ;
2010-07-26 21:08:52 +09:00
smp_rmb ( ) ;
2011-07-16 13:39:09 +09:00
2017-01-15 03:59:29 +01:00
boot_addr = __pa_symbol ( exynos4_secondary_startup ) ;
2012-12-11 13:58:43 +09:00
2015-03-18 14:09:55 +01:00
ret = exynos_set_boot_addr ( core_id , boot_addr ) ;
if ( ret )
2014-05-13 07:13:44 +09:00
goto fail ;
2012-12-11 13:58:43 +09:00
2014-07-16 02:59:18 +09:00
call_firmware_op ( cpu_boot , core_id ) ;
2012-12-11 13:58:43 +09:00
2015-03-27 02:32:56 +09:00
if ( soc_is_exynos3250 ( ) )
dsb_sev ( ) ;
else
arch_send_wakeup_ipi_mask ( cpumask_of ( cpu ) ) ;
2011-07-16 13:39:09 +09:00
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
if ( exynos_pen_release = = - 1 )
2010-07-26 21:08:52 +09:00
break ;
udelay ( 10 ) ;
}
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
if ( exynos_pen_release ! = - 1 )
2015-03-18 14:09:53 +01:00
ret = - ETIMEDOUT ;
2010-07-26 21:08:52 +09:00
/*
* now the secondary core is starting up let it run its
* calibrations , then wait for it to finish
*/
2014-05-13 07:13:44 +09:00
fail :
2010-07-26 21:08:52 +09:00
spin_unlock ( & boot_lock ) ;
ARM: smp: remove arch-provided "pen_release"
Consolidating the "pen_release" stuff amongst the various SoC
implementations gives credence to having a CPU holding pen for
secondary CPUs. However, this is far from the truth.
Many SoC implementations cargo-cult copied various bits of the pen
release implementation from the initial Realview/Versatile Express
implementation without understanding what it was or why it existed.
The reason it existed is because these are _development_ platforms,
and some board firmware is unable to individually control the
startup of secondary CPUs. Moreover, they do not have a way to
power down or reset secondary CPUs for hot-unplug. Hence, the
pen_release implementation was designed for ARM Ltd's development
platforms to provide a working implementation, even though it is
very far from what is required.
It was decided a while back to reduce the duplication by consolidating
the "pen_release" variable, but this only made the situation worse -
we have ended up with several implementations that read this variable
but do not write it - again, showing the cargo-cult mentality at work,
lack of proper review of new code, and in some cases a lack of testing.
While it would be preferable to remove pen_release entirely from the
kernel, this is not possible without help from the SoC maintainers,
which seems to be lacking. However, I want to remove pen_release from
arch code to remove the credence that having it gives.
This patch removes pen_release from the arch code entirely, adding
private per-SoC definitions for it instead, and explicitly stating
that write_pen_release() is cargo-cult copied and should not be
copied any further. Rename write_pen_release() in a similar fashion
as well.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
2018-12-13 14:02:48 +00:00
return exynos_pen_release ! = - 1 ? ret : 0 ;
2010-07-26 21:08:52 +09:00
}
2011-09-08 13:15:22 +01:00
static void __init exynos_smp_prepare_cpus ( unsigned int max_cpus )
2010-07-26 21:08:52 +09:00
{
2014-06-02 21:47:46 -07:00
exynos_sysram_init ( ) ;
2015-03-11 11:13:57 +01:00
exynos_set_delayed_reset_assertion ( true ) ;
2014-06-24 19:43:15 +01:00
if ( read_cpuid_part ( ) = = ARM_CPU_PART_CORTEX_A9 )
2018-05-10 13:02:54 +02:00
exynos_scu_enable ( ) ;
2010-07-26 21:08:52 +09:00
}
2011-09-08 13:15:22 +01:00
2014-09-14 02:49:31 +09:00
# ifdef CONFIG_HOTPLUG_CPU
/*
* platform - specific code to shutdown a CPU
*
* Called with IRQs disabled
*/
2014-09-14 02:49:32 +09:00
static void exynos_cpu_die ( unsigned int cpu )
2014-09-14 02:49:31 +09:00
{
int spurious = 0 ;
2014-09-14 02:49:32 +09:00
u32 mpidr = cpu_logical_map ( cpu ) ;
u32 core_id = MPIDR_AFFINITY_LEVEL ( mpidr , 0 ) ;
2014-09-14 02:49:31 +09:00
v7_exit_coherency_flush ( louis ) ;
platform_do_lowpower ( cpu , & spurious ) ;
/*
* bring this CPU back into the world of cache
* coherency , and then restore interrupts
*/
2014-09-14 02:49:32 +09:00
cpu_leave_lowpower ( core_id ) ;
2014-09-14 02:49:31 +09:00
if ( spurious )
pr_warn ( " CPU%u: %u spurious wakeup calls \n " , cpu , spurious ) ;
}
# endif /* CONFIG_HOTPLUG_CPU */
2015-11-15 10:39:53 +09:00
const struct smp_operations exynos_smp_ops __initconst = {
2011-09-08 13:15:22 +01:00
. smp_prepare_cpus = exynos_smp_prepare_cpus ,
. smp_secondary_init = exynos_secondary_init ,
. smp_boot_secondary = exynos_boot_secondary ,
# ifdef CONFIG_HOTPLUG_CPU
. cpu_die = exynos_cpu_die ,
# endif
} ;