diff options
Diffstat (limited to 'target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c')
-rw-r--r-- | target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c | 806 |
1 files changed, 0 insertions, 806 deletions
diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c deleted file mode 100644 index 4aa27eb444..0000000000 --- a/target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c +++ /dev/null @@ -1,806 +0,0 @@ -/* - * arch/ubicom32/kernel/smp.c - * SMP implementation for Ubicom32 processors. - * - * (C) Copyright 2009, Ubicom, Inc. - * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> - * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> - * Copyright (C) 2001,2004 Grant Grundler <grundler@parisc-linux.org> - * - * This file is part of the Ubicom32 Linux Kernel Port. - * - * The Ubicom32 Linux Kernel Port 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. - * - * The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not, - * see <http://www.gnu.org/licenses/>. - * - * Ubicom32 implementation derived from (with many thanks): - * arch/m68knommu - * arch/blackfin - * arch/parisc - */ - -#include <linux/types.h> -#include <linux/spinlock.h> -#include <linux/slab.h> - -#include <linux/kernel.h> -#include <linux/bootmem.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/smp.h> -#include <linux/kernel_stat.h> -#include <linux/mm.h> -#include <linux/err.h> -#include <linux/delay.h> -#include <linux/bitops.h> -#include <linux/cpu.h> -#include <linux/profile.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/ptrace.h> -#include <linux/unistd.h> -#include <linux/irq.h> - -#include <asm/system.h> -#include <asm/atomic.h> -#include <asm/current.h> -#include <asm/tlbflush.h> -#include <asm/timex.h> -#include <asm/cpu.h> -#include <asm/irq.h> -#include <asm/processor.h> -#include <asm/thread.h> -#include <asm/sections.h> -#include <asm/ip5000.h> - -/* - * Mask the debug printout for IPI because they are too verbose - * for regular debugging. - */ - -// #define DEBUG_SMP 1 -#if !defined(DEBUG_SMP) -#define smp_debug(lvl, ...) -#else -static unsigned int smp_debug_lvl = 50; -#define smp_debug(lvl, printargs...) \ - if (lvl >= smp_debug_lvl) { \ - printk(printargs); \ - } -#endif - -#if !defined(DEBUG_SMP) -#define DEBUG_ASSERT(cond) -#else -#define DEBUG_ASSERT(cond) \ - if (!(cond)) { \ - THREAD_STALL; \ - } -#endif - -/* - * List of IPI Commands (more than one can be set at a time). - */ -enum ipi_message_type { - IPI_NOP, - IPI_RESCHEDULE, - IPI_CALL_FUNC, - IPI_CALL_FUNC_SINGLE, - IPI_CPU_STOP, - IPI_CPU_TIMER, -}; - -/* - * We maintain a hardware thread oriented view of online threads - * and those involved or needing IPI. - */ -static volatile unsigned long smp_online_threads = 0; -static volatile unsigned long smp_needs_ipi = 0; -static volatile unsigned long smp_inside_ipi = 0; -static unsigned long smp_irq_affinity[NR_IRQS]; - -/* - * What do we need to track on a per cpu/thread basis? - */ -DEFINE_PER_CPU(struct cpuinfo_ubicom32, cpu_data); - -/* - * Each thread cpuinfo IPI information is guarded by a lock - * that is kept local to this file. - */ -DEFINE_PER_CPU(spinlock_t, ipi_lock) = SPIN_LOCK_UNLOCKED; - -/* - * The IPI(s) are based on a software IRQ through the LDSR. - */ -unsigned int smp_ipi_irq; - -/* - * Define a spinlock so that only one cpu is able to modify the - * smp_needs_ipi and to set/clear the IRQ at a time. - */ -DEFINE_SPINLOCK(smp_ipi_lock); - -/* - * smp_halt_processor() - * Halt this hardware thread. - */ -static void smp_halt_processor(void) -{ - int cpuid = thread_get_self(); - cpu_clear(smp_processor_id(), cpu_online_map); - local_irq_disable(); - printk(KERN_EMERG "cpu[%d] has halted. It is not OK to turn off power \ - until all cpu's are off.\n", cpuid); - for (;;) { - thread_suspend(); - } -} - -/* - * ipi_interrupt() - * Handle an Interprocessor Interrupt. - */ -static irqreturn_t ipi_interrupt(int irq, void *dev_id) -{ - int cpuid = smp_processor_id(); - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpuid); - unsigned long ops; - - /* - * Count this now; we may make a call that never returns. - */ - p->ipi_count++; - - /* - * We are about to process all ops. If another cpu has stated - * that we need an IPI, we will have already processed it. By - * clearing our smp_needs_ipi, and processing all ops, - * we reduce the number of IPI interrupts. However, this introduces - * the possibility that smp_needs_ipi will be clear and the soft irq - * will have gone off; so we need to make the get_affinity() path - * tolerant of spurious interrupts. - */ - spin_lock(&smp_ipi_lock); - smp_needs_ipi &= ~(1 << p->tid); - spin_unlock(&smp_ipi_lock); - - for (;;) { - /* - * Read the set of IPI commands we should handle. - */ - spinlock_t *lock = &per_cpu(ipi_lock, cpuid); - spin_lock(lock); - ops = p->ipi_pending; - p->ipi_pending = 0; - spin_unlock(lock); - - /* - * If we have no IPI commands to execute, break out. - */ - if (!ops) { - break; - } - - /* - * Execute the set of commands in the ops word, one command - * at a time in no particular order. Strip of each command - * as we execute it. - */ - while (ops) { - unsigned long which = ffz(~ops); - ops &= ~(1 << which); - - BUG_ON(!irqs_disabled()); - switch (which) { - case IPI_NOP: - smp_debug(100, KERN_INFO "cpu[%d]: " - "IPI_NOP\n", cpuid); - break; - - case IPI_RESCHEDULE: - /* - * Reschedule callback. Everything to be - * done is done by the interrupt return path. - */ - smp_debug(200, KERN_INFO "cpu[%d]: " - "IPI_RESCHEDULE\n", cpuid); - break; - - case IPI_CALL_FUNC: - smp_debug(100, KERN_INFO "cpu[%d]: " - "IPI_CALL_FUNC\n", cpuid); - generic_smp_call_function_interrupt(); - break; - - case IPI_CALL_FUNC_SINGLE: - smp_debug(100, KERN_INFO "cpu[%d]: " - "IPI_CALL_FUNC_SINGLE\n", cpuid); - generic_smp_call_function_single_interrupt(); - break; - - case IPI_CPU_STOP: - smp_debug(100, KERN_INFO "cpu[%d]: " - "IPI_CPU_STOP\n", cpuid); - smp_halt_processor(); - break; - -#if !defined(CONFIG_LOCAL_TIMERS) - case IPI_CPU_TIMER: - smp_debug(100, KERN_INFO "cpu[%d]: " - "IPI_CPU_TIMER\n", cpuid); -#if defined(CONFIG_GENERIC_CLOCKEVENTS) - local_timer_interrupt(); -#else - update_process_times(user_mode(get_irq_regs())); - profile_tick(CPU_PROFILING); -#endif -#endif - break; - - default: - printk(KERN_CRIT "cpu[%d]: " - "Unknown IPI: %lu\n", cpuid, which); - - return IRQ_NONE; - } - - /* - * Let in any pending interrupts - */ - BUG_ON(!irqs_disabled()); - local_irq_enable(); - local_irq_disable(); - } - } - return IRQ_HANDLED; -} - -/* - * ipi_send() - * Send an Interprocessor Interrupt. - */ -static void ipi_send(int cpu, enum ipi_message_type op) -{ - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpu); - spinlock_t *lock = &per_cpu(ipi_lock, cpu); - unsigned long flags; - - /* - * We protect the setting of the ipi_pending field and ensure - * that the ipi delivery mechanism and interrupt are atomically - * handled. - */ - spin_lock_irqsave(lock, flags); - p->ipi_pending |= 1 << op; - spin_unlock_irqrestore(lock, flags); - - spin_lock_irqsave(&smp_ipi_lock, flags); - smp_needs_ipi |= (1 << p->tid); - ubicom32_set_interrupt(smp_ipi_irq); - spin_unlock_irqrestore(&smp_ipi_lock, flags); - smp_debug(100, KERN_INFO "cpu[%d]: send: %d\n", cpu, op); -} - -/* - * ipi_send_mask - * Send an IPI to each cpu in mask. - */ -static inline void ipi_send_mask(unsigned int op, const struct cpumask mask) -{ - int cpu; - for_each_cpu_mask(cpu, mask) { - ipi_send(cpu, op); - } -} - -/* - * ipi_send_allbutself() - * Send an IPI to all threads but ourselves. - */ -static inline void ipi_send_allbutself(unsigned int op) -{ - int self = smp_processor_id(); - struct cpumask result; - cpumask_copy(&result, &cpu_online_map); - cpu_clear(self, result); - ipi_send_mask(op, result); -} - -/* - * smp_enable_vector() - */ -static void smp_enable_vector(unsigned int irq) -{ - ubicom32_clear_interrupt(smp_ipi_irq); - ldsr_enable_vector(irq); -} - -/* - * smp_disable_vector() - * Disable the interrupt by clearing the appropriate bit in the - * LDSR Mask Register. - */ -static void smp_disable_vector(unsigned int irq) -{ - ldsr_disable_vector(irq); -} - -/* - * smp_mask_vector() - */ -static void smp_mask_vector(unsigned int irq) -{ - ldsr_mask_vector(irq); -} - -/* - * smp_unmask_vector() - */ -static void smp_unmask_vector(unsigned int irq) -{ - ldsr_unmask_vector(irq); -} - -/* - * smp_end_vector() - * Called once an interrupt is completed (reset the LDSR mask). - */ -static void smp_end_vector(unsigned int irq) -{ - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, smp_processor_id()); - spin_lock(&smp_ipi_lock); - smp_inside_ipi &= ~(1 << p->tid); - if (smp_inside_ipi) { - spin_unlock(&smp_ipi_lock); - return; - } - spin_unlock(&smp_ipi_lock); - ldsr_unmask_vector(irq); - smp_debug(100, KERN_INFO "cpu[%d]: unamesk vector\n", smp_processor_id()); -} - -/* - * Special hanlder functions for SMP. - */ -static struct irq_chip ubicom32_smp_chip = { - .name = "UbicoIPI", - .startup = NULL, - .shutdown = NULL, - .enable = smp_enable_vector, - .disable = smp_disable_vector, - .ack = NULL, - .mask = smp_mask_vector, - .unmask = smp_unmask_vector, - .end = smp_end_vector, -}; - -/* - * smp_reset_ipi() - * None of these cpu(s) got their IPI, turn it back on. - * - * Note: This is called by the LDSR which is not a full - * Linux cpu. Thus you must use the raw form of locks - * because lock debugging will not work on the partial - * cpu nature of the LDSR. - */ -void smp_reset_ipi(unsigned long mask) -{ - __raw_spin_lock(&smp_ipi_lock.raw_lock); - smp_needs_ipi |= mask; - smp_inside_ipi &= ~mask; - ubicom32_set_interrupt(smp_ipi_irq); - __raw_spin_unlock(&smp_ipi_lock.raw_lock); - smp_debug(100, KERN_INFO "smp: reset IPIs for: 0x%x\n", mask); -} - -/* - * smp_get_affinity() - * Choose the thread affinity for this interrupt. - * - * Note: This is called by the LDSR which is not a full - * Linux cpu. Thus you must use the raw form of locks - * because lock debugging will not work on the partial - * cpu nature of the LDSR. - */ -unsigned long smp_get_affinity(unsigned int irq, int *all) -{ - unsigned long mask = 0; - - /* - * Most IRQ(s) are delivered in a round robin fashion. - */ - if (irq != smp_ipi_irq) { - unsigned long result = smp_irq_affinity[irq] & smp_online_threads; - DEBUG_ASSERT(result); - *all = 0; - return result; - } - - /* - * This is an IPI request. Return all cpu(s) scheduled for an IPI. - * We also track those cpu(s) that are going to be "receiving" IPI this - * round. When all CPU(s) have called smp_end_vector(), - * we will unmask the IPI interrupt. - */ - __raw_spin_lock(&smp_ipi_lock.raw_lock); - ubicom32_clear_interrupt(smp_ipi_irq); - if (smp_needs_ipi) { - mask = smp_needs_ipi; - smp_inside_ipi |= smp_needs_ipi; - smp_needs_ipi = 0; - } - __raw_spin_unlock(&smp_ipi_lock.raw_lock); - *all = 1; - return mask; -} - -/* - * smp_set_affinity() - * Set the affinity for this irq but store the value in tid(s). - */ -void smp_set_affinity(unsigned int irq, const struct cpumask *dest) -{ - int cpuid; - unsigned long *paffinity = &smp_irq_affinity[irq]; - - /* - * If none specified, all cpus are allowed. - */ - if (cpus_empty(*dest)) { - *paffinity = 0xffffffff; - return; - } - - /* - * Make sure to clear the old value before setting up the - * list. - */ - *paffinity = 0; - for_each_cpu_mask(cpuid, *dest) { - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpuid); - *paffinity |= (1 << p->tid); - } -} - -/* - * smp_send_stop() - * Send a stop request to all CPU but this one. - */ -void smp_send_stop(void) -{ - ipi_send_allbutself(IPI_CPU_STOP); -} - -/* - * smp_send_timer_all() - * Send all cpu(s) but this one, a request to update times. - */ -void smp_send_timer_all(void) -{ - ipi_send_allbutself(IPI_CPU_TIMER); -} - -/* - * smp_timer_broadcast() - * Use an IPI to broadcast a timer message - */ -void smp_timer_broadcast(const struct cpumask *mask) -{ - ipi_send_mask(IPI_CPU_TIMER, *mask); -} - -/* - * smp_send_reschedule() - * Send a reschedule request to the specified cpu. - */ -void smp_send_reschedule(int cpu) -{ - ipi_send(cpu, IPI_RESCHEDULE); -} - -/* - * arch_send_call_function_ipi() - * Cause each cpu in the mask to call the generic function handler. - */ -void arch_send_call_function_ipi_mask(const struct cpumask *mask) -{ - int cpu; - for_each_cpu_mask(cpu, *mask) { - ipi_send(cpu, IPI_CALL_FUNC); - } -} - -/* - * arch_send_call_function_single_ipi() - * Cause the specified cpu to call the generic function handler. - */ -void arch_send_call_function_single_ipi(int cpu) -{ - ipi_send(cpu, IPI_CALL_FUNC_SINGLE); -} - -/* - * setup_profiling_timer() - * Dummy function created to keep Oprofile happy in the SMP case. - */ -int setup_profiling_timer(unsigned int multiplier) -{ - return 0; -} - -/* - * smp_mainline_start() - * Start a slave thread executing a mainline Linux context. - */ -static void __init smp_mainline_start(void *arg) -{ - int cpuid = smp_processor_id(); - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpuid); - - BUG_ON(p->tid != thread_get_self()); - - /* - * Well, support 2.4 linux scheme as well. - */ - if (cpu_test_and_set(cpuid, cpu_online_map)) { - printk(KERN_CRIT "cpu[%d]: already initialized!\n", cpuid); - smp_halt_processor(); - return; - } - - /* - * Initialise the idle task for this CPU - */ - atomic_inc(&init_mm.mm_count); - current->active_mm = &init_mm; - if (current->mm) { - printk(KERN_CRIT "cpu[%d]: idle task already has memory " - "management\n", cpuid); - smp_halt_processor(); - return; - } - - /* - * TODO: X86 does this prior to calling notify, try to understand why? - */ - preempt_disable(); - -#if defined(CONFIG_GENERIC_CLOCKEVENTS) - /* - * Setup a local timer event so that this cpu will get timer interrupts - */ - if (local_timer_setup(cpuid) == -1) { - printk(KERN_CRIT "cpu[%d]: timer alloc failed\n", cpuid); - smp_halt_processor(); - return; - } -#endif - - /* - * Notify those interested that we are up and alive. This must - * be done before interrupts are enabled. It must also be completed - * before the bootstrap cpu returns from __cpu_up() (see comment - * above cpu_set() of the cpu_online_map). - */ - notify_cpu_starting(cpuid); - - /* - * Indicate that this thread is now online and present. Setting - * cpu_online_map has the side effect of allowing the bootstrap - * cpu to continue along; so anything that MUST be done prior to the - * bootstrap cpu returning from __cpu_up() needs to go above here. - */ - cpu_set(cpuid, cpu_online_map); - cpu_set(cpuid, cpu_present_map); - - /* - * Maintain a thread mapping in addition to the cpu mapping. - */ - smp_online_threads |= (1 << p->tid); - - /* - * Enable interrupts for this thread. - */ - local_irq_enable(); - - /* - * Enter the idle loop and wait for a timer to schedule some work. - */ - printk(KERN_INFO "cpu[%d]: entering cpu_idle()\n", cpuid); - cpu_idle(); - - /* Not Reached */ -} - -/* - * smp_cpus_done() - * Called once the kernel_init() has brought up all cpu(s). - */ -void smp_cpus_done(unsigned int cpu_max) -{ - /* Do Nothing */ -} - -/* - * __cpu_up() - * Called to startup a sepcific cpu. - */ -int __cpuinit __cpu_up(unsigned int cpu) -{ - struct task_struct *idle; - unsigned int *stack; - long timeout; - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpu); - - /* - * Create an idle task for this CPU. - */ - idle = fork_idle(cpu); - if (IS_ERR(idle)) { - panic("cpu[%d]: fork failed\n", cpu); - return -ENOSYS; - } - task_thread_info(idle)->cpu = cpu; - - /* - * Setup the sw_ksp[] to point to this new task. - */ - sw_ksp[p->tid] = (unsigned int)idle->stack; - stack = (unsigned int *)(sw_ksp[p->tid] + PAGE_SIZE - 8); - - /* - * Cause the specified thread to execute our smp_mainline_start - * function as a TYPE_NORMAL thread. - */ - printk(KERN_INFO "cpu[%d]: launching mainline Linux thread\n", cpu); - if (thread_start(p->tid, smp_mainline_start, (void *)NULL, stack, - THREAD_TYPE_NORMAL) == -1) { - printk(KERN_WARNING "cpu[%d]: failed thread_start\n", cpu); - return -ENOSYS; - } - - /* - * Wait for the thread to start up. The thread will set - * the online bit when it is running. Our caller execpts the - * cpu to be online if we return 0. - */ - for (timeout = 0; timeout < 10000; timeout++) { - if (cpu_online(cpu)) { - break; - } - - udelay(100); - barrier(); - continue; - } - - if (!cpu_online(cpu)) { - printk(KERN_CRIT "cpu[%d]: failed to live after %ld us\n", - cpu, timeout * 100); - return -ENOSYS; - } - - printk(KERN_INFO "cpu[%d]: came alive after %ld us\n", - cpu, timeout * 100); - return 0; -} - -/* - * Data used by setup_irq for the IPI. - */ -static struct irqaction ipi_irq = { - .name = "ipi", - .flags = IRQF_DISABLED | IRQF_PERCPU, - .handler = ipi_interrupt, -}; - -/* - * smp_prepare_cpus() - * Mark threads that are available to Linux as possible cpus(s). - */ -void __init smp_prepare_cpus(unsigned int max_cpus) -{ - int i; - - /* - * We will need a software IRQ to send IPI(s). We will use - * a single software IRQ for all IPI(s). - */ - if (irq_soft_alloc(&smp_ipi_irq) < 0) { - panic("no software IRQ is available\n"); - return; - } - - /* - * For the IPI interrupt, we want to use our own chip definition. - * This allows us to define what happens in SMP IPI without affecting - * the performance of the other interrupts. - * - * Next, Register the IPI interrupt function against the soft IRQ. - */ - set_irq_chip(smp_ipi_irq, &ubicom32_smp_chip); - setup_irq(smp_ipi_irq, &ipi_irq); - - /* - * We use the device tree node to determine how many - * free cpus we will have (up to NR_CPUS) and we indicate - * that those cpus are present. - * - * We need to do this very early in the SMP case - * because the Linux init code uses the cpu_present_map. - */ - for_each_possible_cpu(i) { - thread_t tid; - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, i); - - /* - * Skip the bootstrap cpu - */ - if (i == 0) { - continue; - } - - /* - * If we have a free thread left in the mask, - * indicate that the cpu is present. - */ - tid = thread_alloc(); - if (tid == (thread_t)-1) { - break; - } - - /* - * Save the hardware thread id for this cpu. - */ - p->tid = tid; - cpu_set(i, cpu_present_map); - printk(KERN_INFO "cpu[%d]: added to cpu_present_map - tid: %d\n", i, tid); - } -} - -/* - * smp_prepare_boot_cpu() - * Copy the per_cpu data into the appropriate spot for the bootstrap cpu. - * - * The code in boot_cpu_init() has already set the boot cpu's - * state in the possible, present, and online maps. - */ -void __devinit smp_prepare_boot_cpu(void) -{ - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, 0); - - smp_online_threads |= (1 << p->tid); - printk(KERN_INFO "cpu[%d]: bootstrap CPU online - tid: %ld\n", - current_thread_info()->cpu, p->tid); -} - -/* - * smp_setup_processor_id() - * Set the current_thread_info() structure cpu value. - * - * We set the value to the true hardware thread value that we are running on. - * NOTE: this function overrides the weak alias function in main.c - */ -void __init smp_setup_processor_id(void) -{ - struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, 0); - int i; - for_each_cpu_mask(i, CPU_MASK_ALL) - set_cpu_possible(i, true); - - current_thread_info()->cpu = 0; - p->tid = thread_get_self(); -} |