2013-05-21 18:24:11 +04:00
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms 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 .
*
* Copyright ( C ) 2012 ARM Limited
*
* Author : Will Deacon < will . deacon @ arm . com >
*/
# include <linux/init.h>
# include <linux/smp.h>
# include <linux/of.h>
2014-05-07 18:18:36 +04:00
# include <linux/delay.h>
2015-07-31 17:46:19 +03:00
# include <linux/psci.h>
2014-05-07 18:18:36 +04:00
# include <uapi/linux/psci.h>
2013-05-21 18:24:11 +04:00
# include <asm/psci.h>
# include <asm/smp_plat.h>
/*
* psci_smp assumes that the following is true about PSCI :
*
* cpu_suspend Suspend the execution on a CPU
* @ state we don ' t currently describe affinity levels , so just pass 0.
* @ entry_point the first instruction to be executed on return
* returns 0 success , < 0 on failure
*
* cpu_off Power down a CPU
* @ state we don ' t currently describe affinity levels , so just pass 0.
* no return on successful call
*
* cpu_on Power up a CPU
* @ cpuid cpuid of target CPU , as from MPIDR
* @ entry_point the first instruction to be executed on return
* returns 0 success , < 0 on failure
*
* migrate Migrate the context to a different CPU
* @ cpuid cpuid of target CPU , as from MPIDR
* returns 0 success , < 0 on failure
*
*/
extern void secondary_startup ( void ) ;
2013-06-17 23:43:14 +04:00
static int psci_boot_secondary ( unsigned int cpu , struct task_struct * idle )
2013-05-21 18:24:11 +04:00
{
if ( psci_ops . cpu_on )
return psci_ops . cpu_on ( cpu_logical_map ( cpu ) ,
ARM: psci: boot_secondary: replace __pa with virt_to_idmap
On some PAE systems (e.g. TI Keystone), memory is above the 32-bit
addressable limit, and the interconnect provides an aliased view of
parts of physical memory in the 32-bit addressable space. This alias
is strictly for boot time usage, and is not otherwise usable because
of coherency limitations.
In this case, virt_to_phys(secondary_startup) would return the
physical address of the secondary CPU boot entry point, but on such
systems, this would be above the 4GB limit.
A separate function, virt_to_idmap(), has been provided to return a
usable physical address for functions in the identity mapping, and
this must be used in preference to virt_to_phys() or __pa() to find
the physical entry point for functions in the identity mapping range.
For other systems, virt_to_idmap() and virt_to_phys() return identical
physical addresses.
Acked-by: Santosh Shilimkar <ssantosh@kernel.org>
Acked-by: Nicolas Pitre <nico@linaro.org>
Tested-by Vitaly Andrianov <vitalya@ti.com>
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
[Mark: apply rmk's suggested rewording]
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2015-07-31 17:46:18 +03:00
virt_to_idmap ( & secondary_startup ) ) ;
2013-05-21 18:24:11 +04:00
return - ENODEV ;
}
# ifdef CONFIG_HOTPLUG_CPU
2015-07-31 17:46:19 +03:00
int psci_cpu_disable ( unsigned int cpu )
{
/* Fail early if we don't have CPU_OFF support */
if ( ! psci_ops . cpu_off )
return - EOPNOTSUPP ;
/* Trusted OS will deny CPU_OFF */
if ( psci_tos_resident_on ( cpu ) )
return - EPERM ;
return 0 ;
}
2013-05-21 18:24:11 +04:00
void __ref psci_cpu_die ( unsigned int cpu )
{
2015-07-31 17:46:19 +03:00
u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN < <
PSCI_0_2_POWER_STATE_TYPE_SHIFT ;
2013-05-21 18:24:11 +04:00
2015-07-31 17:46:19 +03:00
if ( psci_ops . cpu_off )
psci_ops . cpu_off ( state ) ;
2013-05-21 18:24:11 +04:00
2015-07-31 17:46:19 +03:00
/* We should never return */
panic ( " psci: cpu %d failed to shutdown \n " , cpu ) ;
2013-05-21 18:24:11 +04:00
}
2014-05-07 18:18:36 +04:00
int __ref psci_cpu_kill ( unsigned int cpu )
{
int err , i ;
if ( ! psci_ops . affinity_info )
return 1 ;
/*
* cpu_kill could race with cpu_die and we can
* potentially end up declaring this cpu undead
* while it is dying . So , try again a few times .
*/
for ( i = 0 ; i < 10 ; i + + ) {
err = psci_ops . affinity_info ( cpu_logical_map ( cpu ) , 0 ) ;
if ( err = = PSCI_0_2_AFFINITY_LEVEL_OFF ) {
pr_info ( " CPU%d killed. \n " , cpu ) ;
return 1 ;
}
msleep ( 10 ) ;
pr_info ( " Retrying again to check for CPU kill \n " ) ;
}
pr_warn ( " CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d) \n " ,
cpu , err ) ;
/* Make platform_cpu_kill() fail. */
return 0 ;
}
2013-05-21 18:24:11 +04:00
# endif
bool __init psci_smp_available ( void )
{
/* is cpu_on available at least? */
return ( psci_ops . cpu_on ! = NULL ) ;
}
struct smp_operations __initdata psci_smp_ops = {
. smp_boot_secondary = psci_boot_secondary ,
2013-06-04 02:09:14 +04:00
# ifdef CONFIG_HOTPLUG_CPU
2015-07-31 17:46:19 +03:00
. cpu_disable = psci_cpu_disable ,
2013-05-21 18:24:11 +04:00
. cpu_die = psci_cpu_die ,
2014-05-07 18:18:36 +04:00
. cpu_kill = psci_cpu_kill ,
2013-06-04 02:09:14 +04:00
# endif
2013-05-21 18:24:11 +04:00
} ;