Release 4.14 arch/powerpc/platforms/85xx/smp.c
/*
* Author: Andy Fleming <afleming@freescale.com>
* Kumar Gala <galak@kernel.crashing.org>
*
* Copyright 2006-2008, 2011-2012, 2015 Freescale Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#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 <asm/machdep.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/mpic.h>
#include <asm/cacheflush.h>
#include <asm/dbell.h>
#include <asm/code-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;
};
#ifdef CONFIG_HOTPLUG_CPU
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chen-Hui Zhao | 70 | 67.96% | 3 | 60.00% |
Scott Wood | 33 | 32.04% | 2 | 40.00% |
Total | 103 | 100.00% | 5 | 100.00% |
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 & 0xffffffff);
isync();
tb_valid = 0;
local_irq_restore(flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chen-Hui Zhao | 55 | 100.00% | 2 | 100.00% |
Total | 55 | 100.00% | 2 | 100.00% |
static void smp_85xx_mach_cpu_die(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)
;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chen-Hui Zhao | 69 | 97.18% | 2 | 50.00% |
Michael Ellerman | 1 | 1.41% | 1 | 25.00% |
Kumar Gala | 1 | 1.41% | 1 | 25.00% |
Total | 71 | 100.00% | 4 | 100.00% |
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[cpu].cpu_start = 0;
#endif
return;
}
msleep(20);
}
pr_err("CPU%d didn't die...\n", cpu);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chen-Hui Zhao | 64 | 100.00% | 1 | 100.00% |
Total | 64 | 100.00% | 1 | 100.00% |
#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));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
York Sun | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
York Sun | 46 | 100.00% | 1 | 100.00% |
Total | 46 | 100.00% | 1 | 100.00% |
#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 = *(unsigned long *)fsl_secondary_thread_init;
book3e_start_thread(cpu_thread_in_core(cpu), inia);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy Fleming | 38 | 74.51% | 1 | 33.33% |
Scott Wood | 9 | 17.65% | 1 | 33.33% |
Chen-Hui Zhao | 4 | 7.84% | 1 | 33.33% |
Total | 51 | 100.00% | 3 | 100.00% |
#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
* (0xfffff000), 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);
/* Map the spin table */
if (ioremappable)
spin_table = ioremap_prot(*cpu_rel_addr,
sizeof(struct epapr_spin_table), _PAGE_COHERENT);
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(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
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chen-Hui Zhao | 132 | 47.65% | 3 | 25.00% |
Kumar Gala | 90 | 32.49% | 4 | 33.33% |
Peter Tyser | 33 | 11.91% | 1 | 8.33% |
York Sun | 9 | 3.25% | 1 | 8.33% |
Michael Ellerman | 6 | 2.17% | 1 | 8.33% |
Andy Fleming | 4 | 1.44% | 1 | 8.33% |
Anton Blanchard | 3 | 1.08% | 1 | 8.33% |
Total | 277 | 100.00% | 12 | 100.00% |
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(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[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
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chen-Hui Zhao | 240 | 100.00% | 2 | 100.00% |
Total | 240 | 100.00% | 2 | 100.00% |
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);
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);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matthew McClintock | 32 | 86.49% | 2 | 50.00% |
Chen-Hui Zhao | 3 | 8.11% | 1 | 25.00% |
Kevin Hao | 2 | 5.41% | 1 | 25.00% |
Total | 37 | 100.00% | 4 | 100.00% |
static void mpc85xx_smp_kexec_down(void *arg)
{
if (ppc_md.kexec_cpu_down)
ppc_md.kexec_cpu_down(0,1);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matthew McClintock | 25 | 100.00% | 1 | 100.00% |
Total | 25 | 100.00% | 1 | 100.00% |
#else
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);
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 &&
cpu_thread_in_core(cpu) == 0 &&
cpu_thread_in_core(sibling) != 0) {
disable_threadbit = 2;
disable_cpu = sibling;
}
if (disable_threadbit) {
while (paca[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[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();
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Scott Wood | 191 | 89.67% | 1 | 50.00% |
Tiejun Chen | 22 | 10.33% | 1 | 50.00% |
Total | 213 | 100.00% | 2 | 100.00% |
#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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matthew McClintock | 103 | 95.37% | 3 | 75.00% |
Tiejun Chen | 5 | 4.63% | 1 | 25.00% |
Total | 108 | 100.00% | 4 | 100.00% |
#endif /* CONFIG_KEXEC_CORE */
static void smp_85xx_setup_cpu(int cpu_nr)
{
mpic_setup_this_cpu();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Kevin Hao | 12 | 100.00% | 1 | 100.00% |
Total | 12 | 100.00% | 1 | 100.00% |
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_HOTPLUG_CPU
#ifdef CONFIG_FSL_CORENET_RCPM
fsl_rcpm_init();
#endif
#ifdef CONFIG_FSL_PMC
mpc85xx_setup_pmc();
#endif
if (qoriq_pm_ops) {
smp_85xx_ops.give_timebase = mpc85xx_give_timebase;
smp_85xx_ops.take_timebase = mpc85xx_take_timebase;
ppc_md.cpu_die = smp_85xx_mach_cpu_die;
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
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Kumar Gala | 53 | 34.42% | 2 | 15.38% |
Chen-Hui Zhao | 51 | 33.12% | 4 | 30.77% |
Matthew McClintock | 22 | 14.29% | 2 | 15.38% |
Kevin Hao | 18 | 11.69% | 1 | 7.69% |
Milton D. Miller II | 6 | 3.90% | 1 | 7.69% |
Nicholas Piggin | 2 | 1.30% | 1 | 7.69% |
Laurentiu Tudor | 1 | 0.65% | 1 | 7.69% |
Thiago Jung Bauermann | 1 | 0.65% | 1 | 7.69% |
Total | 154 | 100.00% | 13 | 100.00% |
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chen-Hui Zhao | 749 | 44.56% | 7 | 17.95% |
Scott Wood | 247 | 14.69% | 6 | 15.38% |
Matthew McClintock | 219 | 13.03% | 5 | 12.82% |
Kumar Gala | 190 | 11.30% | 5 | 12.82% |
York Sun | 85 | 5.06% | 1 | 2.56% |
Andy Fleming | 55 | 3.27% | 2 | 5.13% |
Tiejun Chen | 34 | 2.02% | 1 | 2.56% |
Peter Tyser | 33 | 1.96% | 1 | 2.56% |
Kevin Hao | 32 | 1.90% | 2 | 5.13% |
Nicholas Piggin | 7 | 0.42% | 2 | 5.13% |
Michael Ellerman | 7 | 0.42% | 1 | 2.56% |
Milton D. Miller II | 6 | 0.36% | 1 | 2.56% |
Anton Blanchard | 6 | 0.36% | 1 | 2.56% |
Thiago Jung Bauermann | 4 | 0.24% | 1 | 2.56% |
Kyle Moffett | 3 | 0.18% | 1 | 2.56% |
Ingo Molnar | 3 | 0.18% | 1 | 2.56% |
Laurentiu Tudor | 1 | 0.06% | 1 | 2.56% |
Total | 1681 | 100.00% | 39 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.