kernel-fxtec-pro1x/arch/sparc/kernel/sun4m_smp.c

323 lines
7.2 KiB
C
Raw Normal View History

/*
* sun4m SMP support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
*/
#include <linux/interrupt.h>
#include <linux/profile.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include "irq.h"
#include "kernel.h"
#define IRQ_IPI_SINGLE 12
#define IRQ_IPI_MASK 13
#define IRQ_IPI_RESCHED 14
#define IRQ_CROSS_CALL 15
static inline unsigned long
swap_ulong(volatile unsigned long *ptr, unsigned long val)
{
__asm__ __volatile__("swap [%1], %0\n\t" :
"=&r" (val), "=&r" (ptr) :
"0" (val), "1" (ptr));
return val;
}
static void smp4m_ipi_init(void);
static void smp_setup_percpu_timer(void);
void __cpuinit smp4m_callin(void)
{
int cpuid = hard_smp_processor_id();
local_flush_cache_all();
local_flush_tlb_all();
notify_cpu_starting(cpuid);
/* Get our local ticker going. */
smp_setup_percpu_timer();
calibrate_delay();
smp_store_cpu_info(cpuid);
local_flush_cache_all();
local_flush_tlb_all();
/*
* Unblock the master CPU _only_ when the scheduler state
* of all secondary CPUs will be up-to-date, so after
* the SMP initialization the master will be just allowed
* to call the scheduler code.
*/
/* Allow master to continue. */
swap_ulong(&cpu_callin_map[cpuid], 1);
/* XXX: What's up with all the flushes? */
local_flush_cache_all();
local_flush_tlb_all();
/* Fix idle thread fields. */
__asm__ __volatile__("ld [%0], %%g6\n\t"
: : "r" (&current_set[cpuid])
: "memory" /* paranoid */);
/* Attach to the address space of init_task. */
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
while (!cpu_isset(cpuid, smp_commenced_mask))
mb();
local_irq_enable();
set_cpu_online(cpuid, true);
}
/*
* Cycle through the processors asking the PROM to start each one.
*/
void __init smp4m_boot_cpus(void)
{
smp4m_ipi_init();
smp_setup_percpu_timer();
local_flush_cache_all();
}
int __cpuinit smp4m_boot_one_cpu(int i)
{
unsigned long *entry = &sun4m_cpu_startup;
struct task_struct *p;
int timeout;
int cpu_node;
cpu_find_by_mid(i, &cpu_node);
/* Cook up an idler for this guy. */
p = fork_idle(i);
current_set[i] = task_thread_info(p);
/* See trampoline.S for details... */
entry += ((i - 1) * 3);
/*
* Initialize the contexts table
* Since the call to prom_startcpu() trashes the structure,
* we need to re-initialize it for each cpu
*/
smp_penguin_ctable.which_io = 0;
smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
smp_penguin_ctable.reg_size = 0;
/* whirrr, whirrr, whirrrrrrrrr... */
printk(KERN_INFO "Starting CPU %d at %p\n", i, entry);
local_flush_cache_all();
prom_startcpu(cpu_node, &smp_penguin_ctable, 0, (char *)entry);
/* wheee... it's going... */
for (timeout = 0; timeout < 10000; timeout++) {
if (cpu_callin_map[i])
break;
udelay(200);
}
if (!(cpu_callin_map[i])) {
printk(KERN_ERR "Processor %d is stuck.\n", i);
return -ENODEV;
}
local_flush_cache_all();
return 0;
}
void __init smp4m_smp_done(void)
{
int i, first;
int *prev;
/* setup cpu list for irq rotation */
first = 0;
prev = &first;
for_each_online_cpu(i) {
*prev = i;
prev = &cpu_data(i).next;
}
*prev = first;
local_flush_cache_all();
/* Ok, they are spinning and ready to go. */
}
/* Initialize IPIs on the SUN4M SMP machine */
static void __init smp4m_ipi_init(void)
{
}
static void smp4m_ipi_resched(int cpu)
{
set_cpu_int(cpu, IRQ_IPI_RESCHED);
}
static void smp4m_ipi_single(int cpu)
{
set_cpu_int(cpu, IRQ_IPI_SINGLE);
}
static void smp4m_ipi_mask_one(int cpu)
{
set_cpu_int(cpu, IRQ_IPI_MASK);
}
static struct smp_funcall {
smpfunc_t func;
unsigned long arg1;
unsigned long arg2;
unsigned long arg3;
unsigned long arg4;
unsigned long arg5;
unsigned long processors_in[SUN4M_NCPUS]; /* Set when ipi entered. */
unsigned long processors_out[SUN4M_NCPUS]; /* Set when ipi exited. */
} ccall_info;
static DEFINE_SPINLOCK(cross_call_lock);
/* Cross calls must be serialized, at least currently. */
static void smp4m_cross_call(smpfunc_t func, cpumask_t mask, unsigned long arg1,
sparc/kernel/: possible cleanups This patch contains the following possible cleanups: - make the following needlessly global code static: - apc.c: apc_swift_idle() - ebus.c: ebus_blacklist_irq() - ebus.c: fill_ebus_child() - ebus.c: fill_ebus_device() - entry.S: syscall_is_too_hard - etra: tsetup_sun4c_stackchk - head.S: cputyp - head.S: prom_vector_p - idprom.c: Sun_Machines[] - ioport.c: _sparc_find_resource() - ioport.c: create_proc_read_entry() - irq.c: struct sparc_irq[] - rtrap.S: sun4c_rett_stackchk - setup.c: prom_sync_me() - setup.c: boot_flags - sun4c_irq.c: sun4c_sbint_to_irq() - sun4d_irq.c: sbus_tid[] - sun4d_irq.c: struct sbus_actions - sun4d_irq.c: sun4d_sbint_to_irq() - sun4m_irq.c: sun4m_sbint_to_irq() - sun4m_irq.c: sun4m_get_irqmask() - sun4m_irq.c: sun4m_timers - sun4m_smp.c: smp4m_cross_call() - sun4m_smp.c: smp4m_blackbox_id() - sun4m_smp.c: smp4m_blackbox_current() - time.c: sp_clock_typ - time.c: sbus_time_init() - traps.c: instruction_dump() - wof.S: spwin_sun4c_stackchk - wuf.S: sun4c_fwin_stackchk - #if 0 the following unused code: - process.c: sparc_backtrace_lock - process.c: __show_backtrace() - process.c: show_backtrace() - process.c: smp_show_backtrace_all_cpus() - remove the following unused code: - entry.S: __handle_exception - smp.c: smp_num_cpus - smp.c: smp_activated - smp.c: __cpu_number_map[] - smp.c: __cpu_logical_map[] - smp.c: bitops_spinlock - traps.c: trap_curbuf - traps.c: trapbuf[] - traps.c: linux_smp_still_initting - traps.c: thiscpus_tbr - traps.c: thiscpus_mid Signed-off-by: Adrian Bunk <bunk@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
2008-06-05 12:40:58 -06:00
unsigned long arg2, unsigned long arg3,
unsigned long arg4)
{
register int ncpus = SUN4M_NCPUS;
unsigned long flags;
spin_lock_irqsave(&cross_call_lock, flags);
/* Init function glue. */
ccall_info.func = func;
ccall_info.arg1 = arg1;
ccall_info.arg2 = arg2;
ccall_info.arg3 = arg3;
ccall_info.arg4 = arg4;
ccall_info.arg5 = 0;
/* Init receive/complete mapping, plus fire the IPI's off. */
{
register int i;
cpu_clear(smp_processor_id(), mask);
cpus_and(mask, cpu_online_map, mask);
for (i = 0; i < ncpus; i++) {
if (cpu_isset(i, mask)) {
ccall_info.processors_in[i] = 0;
ccall_info.processors_out[i] = 0;
set_cpu_int(i, IRQ_CROSS_CALL);
} else {
ccall_info.processors_in[i] = 1;
ccall_info.processors_out[i] = 1;
}
}
}
{
register int i;
i = 0;
do {
if (!cpu_isset(i, mask))
continue;
while (!ccall_info.processors_in[i])
barrier();
} while (++i < ncpus);
i = 0;
do {
if (!cpu_isset(i, mask))
continue;
while (!ccall_info.processors_out[i])
barrier();
} while (++i < ncpus);
}
spin_unlock_irqrestore(&cross_call_lock, flags);
}
/* Running cross calls. */
void smp4m_cross_call_irq(void)
{
int i = smp_processor_id();
ccall_info.processors_in[i] = 1;
ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
ccall_info.arg4, ccall_info.arg5);
ccall_info.processors_out[i] = 1;
}
void smp4m_percpu_timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs;
int cpu = smp_processor_id();
old_regs = set_irq_regs(regs);
sun4m_clear_profile_irq(cpu);
profile_tick(CPU_PROFILING);
if (!--prof_counter(cpu)) {
int user = user_mode(regs);
irq_enter();
update_process_times(user);
irq_exit();
prof_counter(cpu) = prof_multiplier(cpu);
}
set_irq_regs(old_regs);
}
static void __cpuinit smp_setup_percpu_timer(void)
{
int cpu = smp_processor_id();
prof_counter(cpu) = prof_multiplier(cpu) = 1;
load_profile_irq(cpu, lvl14_resolution);
if (cpu == boot_cpu_id)
sparc32: genirq support The conversion of sparc32 to genirq is based on original work done by David S. Miller. Daniel Hellstrom has helped in the conversion and implemented the shutdowm functionality. Marcel van Nies <morcles@gmail.com> has tested this on Sparc Station 20 Test status: sun4c - not tested sun4m,pci - not tested sun4m,sbus - tested (Sparc Classic, Sparc Station 5, Sparc Station 20) sun4d - not tested leon - tested on various combinations of leon boards, including SMP variants generic Introduce use of GENERIC_HARDIRQS and GENERIC_IRQ_SHOW Allocate 64 IRQs - which is enough even for SS2000 Use a table of irq_bucket to maintain uses IRQs irq_bucket is also used to chain several irq's that must be called when the same intrrupt is asserted Use irq_link to link a interrupt source to the irq All plafforms must now supply their own build_device_irq method handler_irq rewriten to use generic irq support floppy Read FLOPPY_IRQ from platform device Use generic request_irq to register the floppy interrupt Rewrote sparc_floppy_irq to use the generic irq support pcic: Introduce irq_chip Store mask in chip_data for use in mask/unmask functions Add build_device_irq for pcic Use pcic_build_device_irq in pci_time_init allocate virtual irqs in pcic_fill_irq sun4c: Introduce irq_chip Store mask in chip_data for use in mask/unmask functions Add build_device_irq for sun4c Use sun4c_build_device_irq in sun4c_init_timers sun4m: Introduce irq_chip Introduce dedicated mask/unmask methods Introduce sun4m_handler_data that allow easy access to necessary data in the mask/unmask functions Add a helper method to enable profile_timer (used from smp) Added sun4m_build_device_irq Use sun4m_build_device_irq in sun4m_init_timers TODO: There is no replacement for smp_rotate that always scheduled next CPU as interrupt target upon an interrupt sun4d: Introduce irq_chip Introduce dedicated mask/unmask methods Introduce sun4d_handler_data that allow easy access to necessary data in mask/unmask fuctions Rewrote sun4d_handler_irq to use generic irq support TODO: The original implmentation of enable/disable had: if (irq < NR_IRQS) return; The new implmentation does not distingush between SBUS and cpu interrupts. I am no sure what is right here. I assume we need to do something for the cpu interrupts. I have not succeeded booting my sun4d box (with or without this patch) and my understanding of this platfrom is limited. So I would be a bit suprised if this works. leon: Introduce irq_chip Store mask in chip_data for use in mask/unmask functions Add build_device_irq for leon Use leon_build_device_irq in leon_init_timers Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Daniel Hellstrom <daniel@gaisler.com> Tested-by: Daniel Hellstrom <daniel@gaisler.com> Tested-by: Marcel van Nies <morcles@gmail.com> Cc: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: David S. Miller <davem@davemloft.net>
2011-04-18 05:25:44 -06:00
sun4m_unmask_profile_irq();
}
sparc/kernel/: possible cleanups This patch contains the following possible cleanups: - make the following needlessly global code static: - apc.c: apc_swift_idle() - ebus.c: ebus_blacklist_irq() - ebus.c: fill_ebus_child() - ebus.c: fill_ebus_device() - entry.S: syscall_is_too_hard - etra: tsetup_sun4c_stackchk - head.S: cputyp - head.S: prom_vector_p - idprom.c: Sun_Machines[] - ioport.c: _sparc_find_resource() - ioport.c: create_proc_read_entry() - irq.c: struct sparc_irq[] - rtrap.S: sun4c_rett_stackchk - setup.c: prom_sync_me() - setup.c: boot_flags - sun4c_irq.c: sun4c_sbint_to_irq() - sun4d_irq.c: sbus_tid[] - sun4d_irq.c: struct sbus_actions - sun4d_irq.c: sun4d_sbint_to_irq() - sun4m_irq.c: sun4m_sbint_to_irq() - sun4m_irq.c: sun4m_get_irqmask() - sun4m_irq.c: sun4m_timers - sun4m_smp.c: smp4m_cross_call() - sun4m_smp.c: smp4m_blackbox_id() - sun4m_smp.c: smp4m_blackbox_current() - time.c: sp_clock_typ - time.c: sbus_time_init() - traps.c: instruction_dump() - wof.S: spwin_sun4c_stackchk - wuf.S: sun4c_fwin_stackchk - #if 0 the following unused code: - process.c: sparc_backtrace_lock - process.c: __show_backtrace() - process.c: show_backtrace() - process.c: smp_show_backtrace_all_cpus() - remove the following unused code: - entry.S: __handle_exception - smp.c: smp_num_cpus - smp.c: smp_activated - smp.c: __cpu_number_map[] - smp.c: __cpu_logical_map[] - smp.c: bitops_spinlock - traps.c: trap_curbuf - traps.c: trapbuf[] - traps.c: linux_smp_still_initting - traps.c: thiscpus_tbr - traps.c: thiscpus_mid Signed-off-by: Adrian Bunk <bunk@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
2008-06-05 12:40:58 -06:00
static void __init smp4m_blackbox_id(unsigned *addr)
{
int rd = *addr & 0x3e000000;
int rs1 = rd >> 11;
addr[0] = 0x81580000 | rd; /* rd %tbr, reg */
addr[1] = 0x8130200c | rd | rs1; /* srl reg, 0xc, reg */
addr[2] = 0x80082003 | rd | rs1; /* and reg, 3, reg */
}
sparc/kernel/: possible cleanups This patch contains the following possible cleanups: - make the following needlessly global code static: - apc.c: apc_swift_idle() - ebus.c: ebus_blacklist_irq() - ebus.c: fill_ebus_child() - ebus.c: fill_ebus_device() - entry.S: syscall_is_too_hard - etra: tsetup_sun4c_stackchk - head.S: cputyp - head.S: prom_vector_p - idprom.c: Sun_Machines[] - ioport.c: _sparc_find_resource() - ioport.c: create_proc_read_entry() - irq.c: struct sparc_irq[] - rtrap.S: sun4c_rett_stackchk - setup.c: prom_sync_me() - setup.c: boot_flags - sun4c_irq.c: sun4c_sbint_to_irq() - sun4d_irq.c: sbus_tid[] - sun4d_irq.c: struct sbus_actions - sun4d_irq.c: sun4d_sbint_to_irq() - sun4m_irq.c: sun4m_sbint_to_irq() - sun4m_irq.c: sun4m_get_irqmask() - sun4m_irq.c: sun4m_timers - sun4m_smp.c: smp4m_cross_call() - sun4m_smp.c: smp4m_blackbox_id() - sun4m_smp.c: smp4m_blackbox_current() - time.c: sp_clock_typ - time.c: sbus_time_init() - traps.c: instruction_dump() - wof.S: spwin_sun4c_stackchk - wuf.S: sun4c_fwin_stackchk - #if 0 the following unused code: - process.c: sparc_backtrace_lock - process.c: __show_backtrace() - process.c: show_backtrace() - process.c: smp_show_backtrace_all_cpus() - remove the following unused code: - entry.S: __handle_exception - smp.c: smp_num_cpus - smp.c: smp_activated - smp.c: __cpu_number_map[] - smp.c: __cpu_logical_map[] - smp.c: bitops_spinlock - traps.c: trap_curbuf - traps.c: trapbuf[] - traps.c: linux_smp_still_initting - traps.c: thiscpus_tbr - traps.c: thiscpus_mid Signed-off-by: Adrian Bunk <bunk@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
2008-06-05 12:40:58 -06:00
static void __init smp4m_blackbox_current(unsigned *addr)
{
int rd = *addr & 0x3e000000;
int rs1 = rd >> 11;
addr[0] = 0x81580000 | rd; /* rd %tbr, reg */
addr[2] = 0x8130200a | rd | rs1; /* srl reg, 0xa, reg */
addr[4] = 0x8008200c | rd | rs1; /* and reg, 0xc, reg */
}
void __init sun4m_init_smp(void)
{
BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4m_blackbox_id);
BTFIXUPSET_BLACKBOX(load_current, smp4m_blackbox_current);
BTFIXUPSET_CALL(smp_cross_call, smp4m_cross_call, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4m_processor_id, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(smp_ipi_resched, smp4m_ipi_resched, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(smp_ipi_single, smp4m_ipi_single, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(smp_ipi_mask_one, smp4m_ipi_mask_one, BTFIXUPCALL_NORM);
}