// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Author : Andy Fleming < afleming @ freescale . com >
* Kumar Gala < galak @ kernel . crashing . org >
*
* Copyright 2006 - 2008 , 2011 - 2012 , 2015 Freescale Semiconductor Inc .
*/
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/sched/hotplug.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/kexec.h>
#include <linux/highmem.h>
#include <linux/cpu.h>
#include <linux/fsl/guts.h>
#include <linux/pgtable.h>
#include <asm /machdep.h>
#include <asm /page.h>
#include <asm /mpic.h>
#include <asm /cacheflush.h>
#include <asm /dbell.h>
#include <asm /text-patching.h>
#include <asm /cputhreads.h>
#include <asm /fsl_pm.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/mpic.h>
#include "smp.h"
struct epapr_spin_table {
u32 addr_h;
u32 addr_l;
u32 r3_h;
u32 r3_l;
u32 reserved;
u32 pir;
};
static u64 timebase;
static int tb_req;
static int tb_valid;
static void mpc85xx_give_timebase(void )
{
unsigned long flags;
local_irq_save(flags);
hard_irq_disable();
while (!tb_req)
barrier();
tb_req = 0 ;
qoriq_pm_ops->freeze_time_base(true );
#ifdef CONFIG_PPC64
/*
* e5500 / e6500 have a workaround for erratum A - 006958 in place
* that will reread the timebase until TBL is non - zero .
* That would be a bad thing when the timebase is frozen .
*
* Thus , we read it manually , and instead of checking that
* TBL is non - zero , we ensure that TB does not change . We don ' t
* do that for the main mftb implementation , because it requires
* a scratch register
*/
{
u64 prev;
asm volatile ("mfspr %0, %1" : "=r" (timebase) :
"i" (SPRN_TBRL));
do {
prev = timebase;
asm volatile ("mfspr %0, %1" : "=r" (timebase) :
"i" (SPRN_TBRL));
} while (prev != timebase);
}
#else
timebase = get_tb();
#endif
mb();
tb_valid = 1 ;
while (tb_valid)
barrier();
qoriq_pm_ops->freeze_time_base(false );
local_irq_restore(flags);
}
static void mpc85xx_take_timebase(void )
{
unsigned long flags;
local_irq_save(flags);
hard_irq_disable();
tb_req = 1 ;
while (!tb_valid)
barrier();
set_tb(timebase >> 32 , timebase & 0 xffffffff);
isync();
tb_valid = 0 ;
local_irq_restore(flags);
}
#ifdef CONFIG_HOTPLUG_CPU
static void smp_85xx_cpu_offline_self(void )
{
unsigned int cpu = smp_processor_id();
local_irq_disable();
hard_irq_disable();
/* mask all irqs to prevent cpu wakeup */
qoriq_pm_ops->irq_mask(cpu);
idle_task_exit();
mtspr(SPRN_TCR, 0 );
mtspr(SPRN_TSR, mfspr(SPRN_TSR));
generic_set_cpu_dead(cpu);
cur_cpu_spec->cpu_down_flush();
qoriq_pm_ops->cpu_die(cpu);
while (1 )
;
}
static void qoriq_cpu_kill(unsigned int cpu)
{
int i;
for (i = 0 ; i < 500 ; i++) {
if (is_cpu_dead(cpu)) {
#ifdef CONFIG_PPC64
paca_ptrs[cpu]->cpu_start = 0 ;
#endif
return ;
}
msleep(20 );
}
pr_err("CPU%d didn't die...\n" , cpu);
}
#endif
/*
* To keep it compatible with old boot program which uses
* cache - inhibit spin table , we need to flush the cache
* before accessing spin table to invalidate any staled data .
* We also need to flush the cache after writing to spin
* table to push data out .
*/
static inline void flush_spin_table(void *spin_table)
{
flush_dcache_range((ulong)spin_table,
(ulong)spin_table + sizeof (struct epapr_spin_table));
}
static inline u32 read_spin_table_addr_l(void *spin_table)
{
flush_dcache_range((ulong)spin_table,
(ulong)spin_table + sizeof (struct epapr_spin_table));
return in_be32(&((struct epapr_spin_table *)spin_table)->addr_l);
}
#ifdef CONFIG_PPC64
static void wake_hw_thread(void *info)
{
void fsl_secondary_thread_init(void );
unsigned long inia;
int cpu = *(const int *)info;
inia = ppc_function_entry(fsl_secondary_thread_init);
book3e_start_thread(cpu_thread_in_core(cpu), inia);
}
#endif
static int smp_85xx_start_cpu(int cpu)
{
int ret = 0 ;
struct device_node *np;
const u64 *cpu_rel_addr;
unsigned long flags;
int ioremappable;
int hw_cpu = get_hard_smp_processor_id(cpu);
struct epapr_spin_table __iomem *spin_table;
np = of_get_cpu_node(cpu, NULL);
cpu_rel_addr = of_get_property(np, "cpu-release-addr" , NULL);
if (!cpu_rel_addr) {
pr_err("No cpu-release-addr for cpu %d\n" , cpu);
return -ENOENT;
}
/*
* A secondary core could be in a spinloop in the bootpage
* ( 0 xfffff000 ) , somewhere in highmem , or somewhere in lowmem .
* The bootpage and highmem can be accessed via ioremap ( ) , but
* we need to directly access the spinloop if its in lowmem .
*/
ioremappable = *cpu_rel_addr > virt_to_phys(high_memory - 1 );
/* Map the spin table */
if (ioremappable)
spin_table = ioremap_coherent(*cpu_rel_addr,
sizeof (struct epapr_spin_table));
else
spin_table = phys_to_virt(*cpu_rel_addr);
local_irq_save(flags);
hard_irq_disable();
if (qoriq_pm_ops && qoriq_pm_ops->cpu_up_prepare)
qoriq_pm_ops->cpu_up_prepare(cpu);
/* if cpu is not spinning, reset it */
if (read_spin_table_addr_l(spin_table) != 1 ) {
/*
* We don ' t set the BPTR register here since it already points
* to the boot page properly .
*/
mpic_reset_core(cpu);
/*
* wait until core is ready . . .
* We need to invalidate the stale data , in case the boot
* loader uses a cache - inhibited spin table .
*/
if (!spin_event_timeout(
read_spin_table_addr_l(spin_table) == 1 ,
10000 , 100 )) {
pr_err("timeout waiting for cpu %d to reset\n" ,
hw_cpu);
ret = -EAGAIN;
goto err;
}
}
flush_spin_table(spin_table);
out_be32(&spin_table->pir, hw_cpu);
#ifdef CONFIG_PPC64
out_be64((u64 *)(&spin_table->addr_h),
__pa(ppc_function_entry(generic_secondary_smp_init)));
#else
#ifdef CONFIG_PHYS_ADDR_T_64BIT
/*
* We need also to write addr_h to spin table for systems
* in which their physical memory start address was configured
* to above 4 G , otherwise the secondary core can not get
* correct entry to start from .
*/
out_be32(&spin_table->addr_h, __pa(__early_start) >> 32 );
#endif
out_be32(&spin_table->addr_l, __pa(__early_start));
#endif
flush_spin_table(spin_table);
err:
local_irq_restore(flags);
if (ioremappable)
iounmap(spin_table);
return ret;
}
static int smp_85xx_kick_cpu(int nr)
{
int ret = 0 ;
#ifdef CONFIG_PPC64
int primary = nr;
#endif
WARN_ON(nr < 0 || nr >= num_possible_cpus());
pr_debug("kick CPU #%d\n" , nr);
#ifdef CONFIG_PPC64
if (threads_per_core == 2 ) {
if (WARN_ON_ONCE(!cpu_has_feature(CPU_FTR_SMT)))
return -ENOENT;
booting_thread_hwid = cpu_thread_in_core(nr);
primary = cpu_first_thread_sibling(nr);
if (qoriq_pm_ops && qoriq_pm_ops->cpu_up_prepare)
qoriq_pm_ops->cpu_up_prepare(nr);
/*
* If either thread in the core is online , use it to start
* the other .
*/
if (cpu_online(primary)) {
smp_call_function_single(primary,
wake_hw_thread, &nr, 1 );
goto done;
} else if (cpu_online(primary + 1 )) {
smp_call_function_single(primary + 1 ,
wake_hw_thread, &nr, 1 );
goto done;
}
/*
* If getting here , it means both threads in the core are
* offline . So start the primary thread , then it will start
* the thread specified in booting_thread_hwid , the one
* corresponding to nr .
*/
} else if (threads_per_core == 1 ) {
/*
* If one core has only one thread , set booting_thread_hwid to
* an invalid value .
*/
booting_thread_hwid = INVALID_THREAD_HWID;
} else if (threads_per_core > 2 ) {
pr_err("Do not support more than 2 threads per CPU." );
return -EINVAL;
}
ret = smp_85xx_start_cpu(primary);
if (ret)
return ret;
done:
paca_ptrs[nr]->cpu_start = 1 ;
generic_set_cpu_up(nr);
return ret;
#else
ret = smp_85xx_start_cpu(nr);
if (ret)
return ret;
generic_set_cpu_up(nr);
return ret;
#endif
}
struct smp_ops_t smp_85xx_ops = {
.cause_nmi_ipi = NULL,
.kick_cpu = smp_85xx_kick_cpu,
.cpu_bootable = smp_generic_cpu_bootable,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = generic_cpu_disable,
.cpu_die = generic_cpu_die,
#endif
#if defined (CONFIG_KEXEC_CORE) && !defined (CONFIG_PPC64)
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
#endif
};
#ifdef CONFIG_KEXEC_CORE
#ifdef CONFIG_PPC32
atomic_t kexec_down_cpus = ATOMIC_INIT(0 );
static void mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary)
{
local_irq_disable();
if (secondary) {
cur_cpu_spec->cpu_down_flush();
atomic_inc(&kexec_down_cpus);
/* loop forever */
while (1 );
}
}
static void mpc85xx_smp_kexec_down(void *arg)
{
if (ppc_md.kexec_cpu_down)
ppc_md.kexec_cpu_down(0 ,1 );
}
#else
static void mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary)
{
int cpu = smp_processor_id();
int sibling = cpu_last_thread_sibling(cpu);
bool notified = false ;
int disable_cpu;
int disable_threadbit = 0 ;
long start = mftb();
long now;
local_irq_disable();
hard_irq_disable();
mpic_teardown_this_cpu(secondary);
#ifdef CONFIG_CRASH_DUMP
if (cpu == crashing_cpu && cpu_thread_in_core(cpu) != 0 ) {
/*
* We enter the crash kernel on whatever cpu crashed ,
* even if it ' s a secondary thread . If that ' s the case ,
* disable the corresponding primary thread .
*/
disable_threadbit = 1 ;
disable_cpu = cpu_first_thread_sibling(cpu);
} else if (sibling == crashing_cpu) {
return ;
}
#endif
if (cpu_thread_in_core(cpu) == 0 && cpu_thread_in_core(sibling) != 0 ) {
disable_threadbit = 2 ;
disable_cpu = sibling;
}
if (disable_threadbit) {
while (paca_ptrs[disable_cpu]->kexec_state < KEXEC_STATE_REAL_MODE) {
barrier();
now = mftb();
if (!notified && now - start > 1000000 ) {
pr_info("%s/%d: waiting for cpu %d to enter KEXEC_STATE_REAL_MODE (%d)\n" ,
__func__, smp_processor_id(),
disable_cpu,
paca_ptrs[disable_cpu]->kexec_state);
notified = true ;
}
}
if (notified) {
pr_info("%s: cpu %d done waiting\n" ,
__func__, disable_cpu);
}
mtspr(SPRN_TENC, disable_threadbit);
while (mfspr(SPRN_TENSR) & disable_threadbit)
cpu_relax();
}
}
#endif
static void mpc85xx_smp_machine_kexec(struct kimage *image)
{
#ifdef CONFIG_PPC32
int timeout = INT_MAX;
int i, num_cpus = num_present_cpus();
if (image->type == KEXEC_TYPE_DEFAULT)
smp_call_function(mpc85xx_smp_kexec_down, NULL, 0 );
while ( (atomic_read(&kexec_down_cpus) != (num_cpus - 1 )) &&
( timeout > 0 ) )
{
timeout--;
}
if ( !timeout )
printk(KERN_ERR "Unable to bring down secondary cpu(s)" );
for_each_online_cpu(i)
{
if ( i == smp_processor_id() ) continue ;
mpic_reset_core(i);
}
#endif
default_machine_kexec(image);
}
#endif /* CONFIG_KEXEC_CORE */
static void smp_85xx_setup_cpu(int cpu_nr)
{
mpic_setup_this_cpu();
}
void __init mpc85xx_smp_init(void )
{
struct device_node *np;
np = of_find_node_by_type(NULL, "open-pic" );
if (np) {
smp_85xx_ops.probe = smp_mpic_probe;
smp_85xx_ops.setup_cpu = smp_85xx_setup_cpu;
smp_85xx_ops.message_pass = smp_mpic_message_pass;
} else
smp_85xx_ops.setup_cpu = NULL;
if (cpu_has_feature(CPU_FTR_DBELL)) {
/*
* If left NULL , . message_pass defaults to
* smp_muxed_ipi_message_pass
*/
smp_85xx_ops.message_pass = NULL;
smp_85xx_ops.cause_ipi = doorbell_global_ipi;
smp_85xx_ops.probe = NULL;
}
#ifdef CONFIG_FSL_CORENET_RCPM
/* Assign a value to qoriq_pm_ops on PPC_E500MC */
fsl_rcpm_init();
#else
/* Assign a value to qoriq_pm_ops on !PPC_E500MC */
mpc85xx_setup_pmc();
#endif
if (qoriq_pm_ops) {
smp_85xx_ops.give_timebase = mpc85xx_give_timebase;
smp_85xx_ops.take_timebase = mpc85xx_take_timebase;
#ifdef CONFIG_HOTPLUG_CPU
smp_85xx_ops.cpu_offline_self = smp_85xx_cpu_offline_self;
smp_85xx_ops.cpu_die = qoriq_cpu_kill;
#endif
}
smp_ops = &smp_85xx_ops;
#ifdef CONFIG_KEXEC_CORE
ppc_md.kexec_cpu_down = mpc85xx_smp_kexec_cpu_down;
ppc_md.machine_kexec = mpc85xx_smp_machine_kexec;
#endif
}
Messung V0.5 in Prozent C=97 H=73 G=85
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland