Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6becef7e authored by chenhui zhao's avatar chenhui zhao Committed by Scott Wood
Browse files

powerpc/mpc85xx: Add CPU hotplug support for E6500



Support Freescale E6500 core-based platforms, like t4240.
Support disabling/enabling individual CPU thread dynamically.

Signed-off-by: default avatarChenhui Zhao <chenhui.zhao@freescale.com>
parent 2f4f1f81
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
#ifndef _ASM_POWERPC_CPUTHREADS_H
#define _ASM_POWERPC_CPUTHREADS_H

#ifndef __ASSEMBLY__
#include <linux/cpumask.h>

/*
@@ -103,7 +104,12 @@ static inline u32 get_tensr(void)
	return 1;
}

void book3e_start_thread(int thread, unsigned long addr);
void book3e_stop_thread(int thread);

#endif /* __ASSEMBLY__ */

#define INVALID_THREAD_HWID	0x0fff

#endif /* _ASM_POWERPC_CPUTHREADS_H */
+1 −0
Original line number Diff line number Diff line
@@ -200,6 +200,7 @@ extern void generic_secondary_thread_init(void);
extern unsigned long __secondary_hold_spinloop;
extern unsigned long __secondary_hold_acknowledge;
extern char __secondary_hold;
extern unsigned int booting_thread_hwid;

extern void __early_start(void);
#endif /* __ASSEMBLY__ */
+78 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#include <asm/kvm_book3s_asm.h>
#include <asm/ptrace.h>
#include <asm/hw_irq.h>
#include <asm/cputhreads.h>

/* The physical memory is laid out such that the secondary processor
 * spin code sits at 0x0000...0x00ff. On server, the vectors follow
@@ -181,6 +182,45 @@ exception_marker:
#endif

#ifdef CONFIG_PPC_BOOK3E
/*
 * The booting_thread_hwid holds the thread id we want to boot in cpu
 * hotplug case. It is set by cpu hotplug code, and is invalid by default.
 * The thread id is the same as the initial value of SPRN_PIR[THREAD_ID]
 * bit field.
 */
	.globl	booting_thread_hwid
booting_thread_hwid:
	.long  INVALID_THREAD_HWID
	.align 3
/*
 * start a thread in the same core
 * input parameters:
 * r3 = the thread physical id
 * r4 = the entry point where thread starts
 */
_GLOBAL(book3e_start_thread)
	LOAD_REG_IMMEDIATE(r5, MSR_KERNEL)
	cmpi	0, r3, 0
	beq	10f
	cmpi	0, r3, 1
	beq	11f
	/* If the thread id is invalid, just exit. */
	b	13f
10:
	mttmr	TMRN_IMSR0, r5
	mttmr	TMRN_INIA0, r4
	b	12f
11:
	mttmr	TMRN_IMSR1, r5
	mttmr	TMRN_INIA1, r4
12:
	isync
	li	r6, 1
	sld	r6, r6, r3
	mtspr	SPRN_TENS, r6
13:
	blr

/*
 * stop a thread in the same core
 * input parameter:
@@ -280,6 +320,44 @@ _GLOBAL(generic_secondary_smp_init)
	mr	r3,r24
	mr	r4,r25
	bl	book3e_secondary_core_init

/*
 * After common core init has finished, check if the current thread is the
 * one we wanted to boot. If not, start the specified thread and stop the
 * current thread.
 */
	LOAD_REG_ADDR(r4, booting_thread_hwid)
	lwz     r3, 0(r4)
	li	r5, INVALID_THREAD_HWID
	cmpw	r3, r5
	beq	20f

	/*
	 * The value of booting_thread_hwid has been stored in r3,
	 * so make it invalid.
	 */
	stw	r5, 0(r4)

	/*
	 * Get the current thread id and check if it is the one we wanted.
	 * If not, start the one specified in booting_thread_hwid and stop
	 * the current thread.
	 */
	mfspr	r8, SPRN_TIR
	cmpw	r3, r8
	beq	20f

	/* start the specified thread */
	LOAD_REG_ADDR(r5, fsl_secondary_thread_init)
	ld	r4, 0(r5)
	bl	book3e_start_thread

	/* stop the current thread */
	mr	r3, r8
	bl	book3e_stop_thread
10:
	b	10b
20:
#endif

generic_secondary_common_init:
+39 −31
Original line number Diff line number Diff line
@@ -180,24 +180,11 @@ static inline u32 read_spin_table_addr_l(void *spin_table)
static void wake_hw_thread(void *info)
{
	void fsl_secondary_thread_init(void);
	unsigned long imsr, inia;
	int nr = *(const int *)info;
	unsigned long inia;
	int cpu = *(const int *)info;

	imsr = MSR_KERNEL;
	inia = *(unsigned long *)fsl_secondary_thread_init;

	if (cpu_thread_in_core(nr) == 0) {
		/* For when we boot on a secondary thread with kdump */
		mttmr(TMRN_IMSR0, imsr);
		mttmr(TMRN_INIA0, inia);
		mtspr(SPRN_TENS, TEN_THREAD(0));
	} else {
		mttmr(TMRN_IMSR1, imsr);
		mttmr(TMRN_INIA1, inia);
		mtspr(SPRN_TENS, TEN_THREAD(1));
	}

	smp_generic_kick_cpu(nr);
	book3e_start_thread(cpu_thread_in_core(cpu), inia);
}
#endif

@@ -292,33 +279,54 @@ static int smp_85xx_kick_cpu(int nr)
	pr_debug("kick CPU #%d\n", nr);

#ifdef CONFIG_PPC64
	/* Threads don't use the spin table */
	if (cpu_thread_in_core(nr) != 0) {
		int primary = cpu_first_thread_sibling(nr);

	if (threads_per_core == 2) {
		if (WARN_ON_ONCE(!cpu_has_feature(CPU_FTR_SMT)))
			return -ENOENT;

		if (cpu_thread_in_core(nr) != 1) {
			pr_err("%s: cpu %d: invalid hw thread %d\n",
			       __func__, nr, cpu_thread_in_core(nr));
			return -ENOENT;
		}
		booting_thread_hwid = cpu_thread_in_core(nr);
		primary = cpu_first_thread_sibling(nr);

		if (!cpu_online(primary)) {
			pr_err("%s: cpu %d: primary %d not online\n",
			       __func__, nr, primary);
			return -ENOENT;
		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;
		}

		smp_call_function_single(primary, wake_hw_thread, &nr, 0);
		return 0;
		/*
		 * 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);