cregit-Linux how code gets into the kernel

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

PersonTokensPropCommitsCommitProp
Chen-Hui Zhao7067.96%360.00%
Scott Wood3332.04%240.00%
Total103100.00%5100.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

PersonTokensPropCommitsCommitProp
Chen-Hui Zhao55100.00%2100.00%
Total55100.00%2100.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

PersonTokensPropCommitsCommitProp
Chen-Hui Zhao6997.18%250.00%
Michael Ellerman11.41%125.00%
Kumar Gala11.41%125.00%
Total71100.00%4100.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

PersonTokensPropCommitsCommitProp
Chen-Hui Zhao64100.00%1100.00%
Total64100.00%1100.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

PersonTokensPropCommitsCommitProp
York Sun30100.00%1100.00%
Total30100.00%1100.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

PersonTokensPropCommitsCommitProp
York Sun46100.00%1100.00%
Total46100.00%1100.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

PersonTokensPropCommitsCommitProp
Andy Fleming3874.51%133.33%
Scott Wood917.65%133.33%
Chen-Hui Zhao47.84%133.33%
Total51100.00%3100.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

PersonTokensPropCommitsCommitProp
Chen-Hui Zhao13247.65%325.00%
Kumar Gala9032.49%433.33%
Peter Tyser3311.91%18.33%
York Sun93.25%18.33%
Michael Ellerman62.17%18.33%
Andy Fleming41.44%18.33%
Anton Blanchard31.08%18.33%
Total277100.00%12100.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

PersonTokensPropCommitsCommitProp
Chen-Hui Zhao240100.00%2100.00%
Total240100.00%2100.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

PersonTokensPropCommitsCommitProp
Matthew McClintock3286.49%250.00%
Chen-Hui Zhao38.11%125.00%
Kevin Hao25.41%125.00%
Total37100.00%4100.00%


static void mpc85xx_smp_kexec_down(void *arg) { if (ppc_md.kexec_cpu_down) ppc_md.kexec_cpu_down(0,1); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew McClintock25100.00%1100.00%
Total25100.00%1100.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

PersonTokensPropCommitsCommitProp
Scott Wood19189.67%150.00%
Tiejun Chen2210.33%150.00%
Total213100.00%2100.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

PersonTokensPropCommitsCommitProp
Matthew McClintock10395.37%375.00%
Tiejun Chen54.63%125.00%
Total108100.00%4100.00%

#endif /* CONFIG_KEXEC_CORE */
static void smp_85xx_setup_cpu(int cpu_nr) { mpic_setup_this_cpu(); }

Contributors

PersonTokensPropCommitsCommitProp
Kevin Hao12100.00%1100.00%
Total12100.00%1100.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

PersonTokensPropCommitsCommitProp
Kumar Gala5334.42%215.38%
Chen-Hui Zhao5133.12%430.77%
Matthew McClintock2214.29%215.38%
Kevin Hao1811.69%17.69%
Milton D. Miller II63.90%17.69%
Nicholas Piggin21.30%17.69%
Laurentiu Tudor10.65%17.69%
Thiago Jung Bauermann10.65%17.69%
Total154100.00%13100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Chen-Hui Zhao74944.56%717.95%
Scott Wood24714.69%615.38%
Matthew McClintock21913.03%512.82%
Kumar Gala19011.30%512.82%
York Sun855.06%12.56%
Andy Fleming553.27%25.13%
Tiejun Chen342.02%12.56%
Peter Tyser331.96%12.56%
Kevin Hao321.90%25.13%
Nicholas Piggin70.42%25.13%
Michael Ellerman70.42%12.56%
Milton D. Miller II60.36%12.56%
Anton Blanchard60.36%12.56%
Thiago Jung Bauermann40.24%12.56%
Kyle Moffett30.18%12.56%
Ingo Molnar30.18%12.56%
Laurentiu Tudor10.06%12.56%
Total1681100.00%39100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.