b9f1cd71db
The doorbells use the content of the PIR register to match messages from other CPUs. This may or may not be the same as our linux CPU number, so using that as the "target" is no right. Instead, we sample the PIR register at boot on every processor and use that value subsequently when sending IPIs. We also use a per-cpu message mask rather than a global array which should limit cache line contention. Note: We could use the CPU number in the device-tree instead of the PIR register, as they are supposed to be equivalent. This might prove useful if doorbells are to be used to kick CPUs out of FW at boot time, thus before we can sample the PIR. This is however not the case now and using the PIR just works. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
127 lines
3.1 KiB
C
127 lines
3.1 KiB
C
/*
|
|
* Author: Andy Fleming <afleming@freescale.com>
|
|
* Kumar Gala <galak@kernel.crashing.org>
|
|
*
|
|
* Copyright 2006-2008 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/init.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/of.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 <sysdev/fsl_soc.h>
|
|
|
|
extern void __early_start(void);
|
|
|
|
#define BOOT_ENTRY_ADDR_UPPER 0
|
|
#define BOOT_ENTRY_ADDR_LOWER 1
|
|
#define BOOT_ENTRY_R3_UPPER 2
|
|
#define BOOT_ENTRY_R3_LOWER 3
|
|
#define BOOT_ENTRY_RESV 4
|
|
#define BOOT_ENTRY_PIR 5
|
|
#define BOOT_ENTRY_R6_UPPER 6
|
|
#define BOOT_ENTRY_R6_LOWER 7
|
|
#define NUM_BOOT_ENTRY 8
|
|
#define SIZE_BOOT_ENTRY (NUM_BOOT_ENTRY * sizeof(u32))
|
|
|
|
static void __init
|
|
smp_85xx_kick_cpu(int nr)
|
|
{
|
|
unsigned long flags;
|
|
const u64 *cpu_rel_addr;
|
|
__iomem u32 *bptr_vaddr;
|
|
struct device_node *np;
|
|
int n = 0;
|
|
int ioremappable;
|
|
|
|
WARN_ON (nr < 0 || nr >= NR_CPUS);
|
|
|
|
pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr);
|
|
|
|
np = of_get_cpu_node(nr, NULL);
|
|
cpu_rel_addr = of_get_property(np, "cpu-release-addr", NULL);
|
|
|
|
if (cpu_rel_addr == NULL) {
|
|
printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
bptr_vaddr = ioremap(*cpu_rel_addr, SIZE_BOOT_ENTRY);
|
|
else
|
|
bptr_vaddr = phys_to_virt(*cpu_rel_addr);
|
|
|
|
local_irq_save(flags);
|
|
|
|
out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr);
|
|
out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start));
|
|
|
|
if (!ioremappable)
|
|
flush_dcache_range((ulong)bptr_vaddr,
|
|
(ulong)(bptr_vaddr + SIZE_BOOT_ENTRY));
|
|
|
|
/* Wait a bit for the CPU to ack. */
|
|
while ((__secondary_hold_acknowledge != nr) && (++n < 1000))
|
|
mdelay(1);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
if (ioremappable)
|
|
iounmap(bptr_vaddr);
|
|
|
|
pr_debug("waited %d msecs for CPU #%d.\n", n, nr);
|
|
}
|
|
|
|
static void __init
|
|
smp_85xx_setup_cpu(int cpu_nr)
|
|
{
|
|
mpic_setup_this_cpu();
|
|
if (cpu_has_feature(CPU_FTR_DBELL))
|
|
doorbell_setup_this_cpu();
|
|
}
|
|
|
|
struct smp_ops_t smp_85xx_ops = {
|
|
.kick_cpu = smp_85xx_kick_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;
|
|
}
|
|
|
|
if (cpu_has_feature(CPU_FTR_DBELL))
|
|
smp_85xx_ops.message_pass = doorbell_message_pass;
|
|
|
|
BUG_ON(!smp_85xx_ops.message_pass);
|
|
|
|
smp_ops = &smp_85xx_ops;
|
|
}
|