smp.c 4.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 * Copyright (C) 2001, 2002, 2003 Broadcom Corporation
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/kernel_stat.h>

#include <asm/mmu_context.h>
#include <asm/io.h>
27
#include <asm/fw/cfe/cfe_api.h>
L
Linus Torvalds 已提交
28 29 30 31 32
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_int.h>

static void *mailbox_set_regs[] = {
33 34
	IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_SET_CPU),
	IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_SET_CPU)
L
Linus Torvalds 已提交
35 36 37
};

static void *mailbox_clear_regs[] = {
38 39
	IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CLR_CPU),
	IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_CLR_CPU)
L
Linus Torvalds 已提交
40 41 42
};

static void *mailbox_regs[] = {
43 44
	IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CPU),
	IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_CPU)
L
Linus Torvalds 已提交
45 46 47 48 49
};

/*
 * SMP init and finish on secondary CPUs
 */
50
void __cpuinit sb1250_smp_init(void)
L
Linus Torvalds 已提交
51 52 53 54 55 56 57 58
{
	unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
		STATUSF_IP1 | STATUSF_IP0;

	/* Set interrupt mask, but don't enable */
	change_c0_status(ST0_IM, imask);
}

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
/*
 * These are routines for dealing with the sb1250 smp capabilities
 * independent of board/firmware
 */

/*
 * Simple enough; everything is set up, so just poke the appropriate mailbox
 * register, and we should be set
 */
static void sb1250_send_ipi_single(int cpu, unsigned int action)
{
	__raw_writeq((((u64)action) << 48), mailbox_set_regs[cpu]);
}

static inline void sb1250_send_ipi_mask(cpumask_t mask, unsigned int action)
{
	unsigned int i;

	for_each_cpu_mask(i, mask)
		sb1250_send_ipi_single(i, action);
}

/*
 * Code to run on secondary just after probing the CPU
 */
static void __cpuinit sb1250_init_secondary(void)
{
	extern void sb1250_smp_init(void);

	sb1250_smp_init();
}

/*
 * Do any tidying up before marking online and running the idle
 * loop
 */
static void __cpuinit sb1250_smp_finish(void)
L
Linus Torvalds 已提交
96
{
97 98 99
	extern void sb1250_clockevent_init(void);

	sb1250_clockevent_init();
L
Linus Torvalds 已提交
100 101 102 103
	local_irq_enable();
}

/*
104
 * Final cleanup after all secondaries booted
L
Linus Torvalds 已提交
105
 */
106 107 108
static void sb1250_cpus_done(void)
{
}
L
Linus Torvalds 已提交
109 110

/*
111 112
 * Setup the PC, SP, and GP of a secondary processor and start it
 * running!
L
Linus Torvalds 已提交
113
 */
114
static void __cpuinit sb1250_boot_secondary(int cpu, struct task_struct *idle)
L
Linus Torvalds 已提交
115
{
116 117 118 119 120 121 122
	int retval;

	retval = cfe_cpu_start(cpu_logical_map(cpu), &smp_bootstrap,
			       __KSTK_TOS(idle),
			       (unsigned long)task_thread_info(idle), 0);
	if (retval != 0)
		printk("cfe_start_cpu(%i) returned %i\n" , cpu, retval);
L
Linus Torvalds 已提交
123 124
}

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
/*
 * Use CFE to find out how many CPUs are available, setting up
 * phys_cpu_present_map and the logical/physical mappings.
 * XXXKW will the boot CPU ever not be physical 0?
 *
 * Common setup before any secondaries are started
 */
static void __init sb1250_smp_setup(void)
{
	int i, num;

	cpus_clear(phys_cpu_present_map);
	cpu_set(0, phys_cpu_present_map);
	__cpu_number_map[0] = 0;
	__cpu_logical_map[0] = 0;

	for (i = 1, num = 0; i < NR_CPUS; i++) {
		if (cfe_cpu_stop(i) == 0) {
			cpu_set(i, phys_cpu_present_map);
			__cpu_number_map[i] = ++num;
			__cpu_logical_map[num] = i;
		}
	}
	printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
}

static void __init sb1250_prepare_cpus(unsigned int max_cpus)
{
}

struct plat_smp_ops sb_smp_ops = {
	.send_ipi_single	= sb1250_send_ipi_single,
	.send_ipi_mask		= sb1250_send_ipi_mask,
	.init_secondary		= sb1250_init_secondary,
	.smp_finish		= sb1250_smp_finish,
	.cpus_done		= sb1250_cpus_done,
	.boot_secondary		= sb1250_boot_secondary,
	.smp_setup		= sb1250_smp_setup,
	.prepare_cpus		= sb1250_prepare_cpus,
};

166
void sb1250_mailbox_interrupt(void)
L
Linus Torvalds 已提交
167 168 169 170 171 172
{
	int cpu = smp_processor_id();
	unsigned int action;

	kstat_this_cpu.irqs[K_INT_MBOX_0]++;
	/* Load the mailbox register to figure out what we're supposed to do */
173
	action = (____raw_readq(mailbox_regs[cpu]) >> 48) & 0xffff;
L
Linus Torvalds 已提交
174 175

	/* Clear the mailbox to clear the interrupt */
176
	____raw_writeq(((u64)action) << 48, mailbox_clear_regs[cpu]);
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185

	/*
	 * Nothing to do for SMP_RESCHEDULE_YOURSELF; returning from the
	 * interrupt will do the reschedule for us
	 */

	if (action & SMP_CALL_FUNCTION)
		smp_call_function_interrupt();
}