[PATCH V4 07/62] ST SPEAr13XX: Adding machine specific src files

Russell King - ARM Linux linux at arm.linux.org.uk
Tue Jan 18 11:06:55 EST 2011


On Tue, Jan 18, 2011 at 12:41:35PM +0530, Viresh Kumar wrote:
> diff --git a/arch/arm/mach-spear13xx/headsmp.S b/arch/arm/mach-spear13xx/headsmp.S
> new file mode 100644
> index 0000000..30761d3
> --- /dev/null
> +++ b/arch/arm/mach-spear13xx/headsmp.S
> @@ -0,0 +1,96 @@
> +/*
> + * arch/arm/mach-spear13XX/headsmp.S
> + *
> + * Picked from realview
> + * Copyright (c) 2010 ST Microelectronics Limited
> + * Shiraz Hashim <shiraz.hashim at st.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/linkage.h>
> +#include <linux/init.h>
> +
> +	__INIT

Is this ever called after the kernel text is thrown away?  What if we
add support in the generic code to start with secondary CPUs offline
as a power saving or boot time feature?

> +
> +/*
> + * This one is picked from Tegra :-
> + *
> + * The secondary kernel init calls v7_flush_dcache_all before it enables
> + * the L1; however, the L1 comes out of reset in an undefined state, so
> + * the clean + invalidate performed by v7_flush_dcache_all causes a bunch
> + * of cache lines with uninitialized data and uninitialized tags to get
> + * written out to memory, which does really unpleasant things to the ain
> + * processor. We fix this by performing an invalidate, rather than a
> + * clean + invalidate, before jumping into the kernel.
> + */
> +ENTRY(v7_invalidate_l1)
> +	mov	r0, #0
> +	mcr	p15, 2, r0, c0, c0, 0
> +	mrc	p15, 1, r0, c0, c0, 0
> +
> +	ldr	r1, =0x7fff
> +	and	r2, r1, r0, lsr #13
> +
> +	ldr	r1, =0x3ff
> +
> +	and	r3, r1, r0, lsr #3	@ NumWays - 1
> +	add	r2, r2, #1	@ NumSets
> +
> +	and	r0, r0, #0x7
> +	add	r0, r0, #4	@ SetShift
> +
> +	clz	r1, r3		@ WayShift
> +	add	r4, r3, #1	@ NumWays
> +1:	sub	r2, r2, #1	@ NumSets--
> +	mov	r3, r4		@ Temp = NumWays
> +2:	subs	r3, r3, #1	@ Temp--
> +	mov	r5, r3, lsl r1
> +	mov	r6, r2, lsl r0
> +	orr	r5, r5, r6	@ Reg = Temp<<WayShift)|(NumSets<<SetShift)
> +	mcr	p15, 0, r5, c7, c6, 2
> +	bgt	2b
> +	cmp	r2, #0
> +	bgt	1b
> +	dsb
> +	isb
> +	mov	pc, lr
> +ENDPROC(v7_invalidate_l1)

This code appears to have its only caller commented out.  Should it be
removed?

> +
> +/*
> + * spear13xx specific entry point for secondary CPUs. This provides
> + * a "holding pen" into which all secondary cores are held until we're
> + * ready for them to initialise.
> + */
> +ENTRY(spear13xx_secondary_startup)
> +	/* If we don't do this then we have a crash */
> +
> +	/*
> +	 * Since now this is being called from xloader so removing it
> +	 * here
> +	 */
> +#if 0
> +	bl v7_invalidate_l1
> +#endif
> +
> +	mrc	p15, 0, r0, c0, c0, 5
> +	and	r0, r0, #15
> +	adr	r4, 1f
> +	ldmia	r4, {r5, r6}
> +	sub	r4, r4, r5
> +	add	r6, r6, r4
> +pen:	ldr	r7, [r6]
> +	cmp	r7, r0
> +	bne	pen
> +
> +	/*
> +	 * we've been released from the holding pen: secondary_stack
> +	 * should now contain the SVC stack for this core
> +	 */
> +	b	secondary_startup
> +
> +	.align
> +1:	.long	.
> +	.long	pen_release
...
> +volatile int __cpuinitdata pen_release = -1;
> +static DEFINE_SPINLOCK(boot_lock);
> +
> +/*
> + * Write pen_release in a way that is guaranteed to be visible to all
> + * observers, irrespective of whether they're taking part in coherency
> + * or not. This is necessary for the hotplug code to work reliably.
> + */
> +static void write_pen_release(int val)
> +{
> +	pen_release = val;
> +	smp_wmb();
> +	__cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
> +	outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
> +}
> +
> +static void __iomem *scu_base_addr(void)
> +{
> +	return __io_address(SPEAR13XX_SCU_BASE);
> +}
> +
> +void __cpuinit platform_secondary_init(unsigned int cpu)
> +{
> +	/*
> +	 * if any interrupts are already enabled for the primary
> +	 * core (e.g. timer irq), then they will not have been enabled
> +	 * for us: do so
> +	 */
> +	gic_secondary_init(0);
> +
> +	/*
> +	 * let the primary processor know we're out of the
> +	 * pen, then head off into the C entry point
> +	 */
> +	write_pen_release(-1);
> +
> +	/*
> +	 * Synchronise with the boot thread.
> +	 */
> +	spin_lock(&boot_lock);
> +	spin_unlock(&boot_lock);
> +}
> +
> +int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
> +{
> +	unsigned long timeout;
> +
> +	/*
> +	 * set synchronisation state between this boot processor
> +	 * and the secondary one
> +	 */
> +	spin_lock(&boot_lock);
> +
> +	/*
> +	 * The secondary processor is waiting to be released from
> +	 * the holding pen - release it, then wait for it to flag
> +	 * that it has been released by resetting pen_release.
> +	 *
> +	 * Note that "pen_release" is the hardware CPU ID, whereas
> +	 * "cpu" is Linux's internal ID.
> +	 */
> +	write_pen_release(cpu);
> +
> +	timeout = jiffies + (1 * HZ);
> +	while (time_before(jiffies, timeout)) {
> +		smp_rmb();
> +		if (pen_release == -1)
> +			break;
> +
> +		udelay(10);
> +	}
> +
> +	/*
> +	 * now the secondary core is starting up let it run its
> +	 * calibrations, then wait for it to finish
> +	 */
> +	spin_unlock(&boot_lock);
> +
> +	return pen_release != -1 ? -ENOSYS : 0;
> +}
> +
> +/*
> + * Initialise the CPU possible map early - this describes the CPUs
> + * which may be present or become present in the system.
> + */
> +void __init smp_init_cpus(void)
> +{
> +	void __iomem *scu_base = scu_base_addr();
> +	unsigned int i, ncores;
> +
> +	ncores = scu_base ? scu_get_core_count(scu_base) : 1;
> +
> +	for (i = 0; i < ncores; i++)
> +		set_cpu_possible(i, true);
> +}
> +
> +static void __init wakeup_secondary(void)
> +{
> +	/* nobody is to be released from the pen yet */
> +	pen_release = -1;

But pen_release starts off as -1, so is this really needed?

> +
> +	/*
> +	 * Write the address of secondary startup into the system-wide
> +	 * location (presently it is in SRAM). The BootMonitor waits
> +	 * for this register to become non-zero.
> +	 * We must also send an sev to wake it up
> +	 */
> +	__raw_writel(BSYM(virt_to_phys(spear13xx_secondary_startup)),
> +			__io_address(SPEAR13XX_SYS_LOCATION));
> +
> +	mb();

Do you really need to sync back to L2, or will a dsb() do here - and
as the spinlock code uses dsb() + sev() together, would it make sense
to combine the two?  (dsb() is required to ensure all previous writes
are visible prior to the sev() executing.)




More information about the linux-arm-kernel mailing list