/* SMP support routines. * * Copyright (C) 2006-2008 Panasonic Corporation * All Rights Reserved. * * 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. * * 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. */ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/init.h> #include <linux/jiffies.h> #include <linux/cpumask.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/sched.h> #include <linux/profile.h> #include <linux/smp.h> #include <linux/cpu.h> #include <asm/tlbflush.h> #include <asm/bitops.h> #include <asm/processor.h> #include <asm/bug.h> #include <asm/exceptions.h> #include <asm/hardirq.h> #include <asm/fpu.h> #include <asm/mmu_context.h> #include <asm/thread_info.h> #include <asm/cpu-regs.h> #include <asm/intctl-regs.h> #include "internal.h" #ifdef CONFIG_HOTPLUG_CPU #include <asm/cacheflush.h> static unsigned long sleep_mode[NR_CPUS]; static void run_sleep_cpu(unsigned int cpu); static void run_wakeup_cpu(unsigned int cpu); #endif /* CONFIG_HOTPLUG_CPU */ /* * Debug Message function */ #undef DEBUG_SMP #ifdef DEBUG_SMP #define Dprintk(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) #else #define Dprintk(fmt, ...) no_printk(KERN_DEBUG fmt, ##__VA_ARGS__) #endif /* timeout value in msec for smp_nmi_call_function. zero is no timeout. */ #define CALL_FUNCTION_NMI_IPI_TIMEOUT 0 /* * Structure and data for smp_nmi_call_function(). */ struct nmi_call_data_struct { smp_call_func_t func; void *info; cpumask_t started; cpumask_t finished; int wait; char size_alignment[0] __attribute__ ((__aligned__(SMP_CACHE_BYTES))); } __attribute__ ((__aligned__(SMP_CACHE_BYTES))); static DEFINE_SPINLOCK(smp_nmi_call_lock); static struct nmi_call_data_struct *nmi_call_data; /* * Data structures and variables */ static cpumask_t cpu_callin_map; /* Bitmask of callin CPUs */ static cpumask_t cpu_callout_map; /* Bitmask of callout CPUs */ cpumask_t cpu_boot_map; /* Bitmask of boot APs */ unsigned long start_stack[NR_CPUS - 1]; /* * Per CPU parameters */ struct mn10300_cpuinfo cpu_data[NR_CPUS] __cacheline_aligned; static int cpucount; /* The count of boot CPUs */ static cpumask_t smp_commenced_mask; cpumask_t cpu_initialized __initdata = CPU_MASK_NONE; /* * Function Prototypes */ static int do_boot_cpu(int); static void smp_show_cpu_info(int cpu_id); static void smp_callin(void); static void smp_online(void); static void smp_store_cpu_info(int); static void smp_cpu_init(void); static void smp_tune_scheduling(void); static void send_IPI_mask(const cpumask_t *cpumask, int irq); static void init_ipi(void); /* * IPI Initialization interrupt definitions */ static void mn10300_ipi_disable(unsigned int irq); static void mn10300_ipi_enable(unsigned int irq); static void mn10300_ipi_chip_disable(struct irq_data *d); static void mn10300_ipi_chip_enable(struct irq_data *d); static void mn10300_ipi_ack(struct irq_data *d); static void mn10300_ipi_nop(struct irq_data *d); static struct irq_chip mn10300_ipi_type = { .name = "cpu_ipi", .irq_disable = mn10300_ipi_chip_disable, .irq_enable = mn10300_ipi_chip_enable, .irq_ack = mn10300_ipi_ack, .irq_eoi = mn10300_ipi_nop }; static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id); static irqreturn_t smp_call_function_interrupt(int irq, void *dev_id); static struct irqaction reschedule_ipi = { .handler = smp_reschedule_interrupt, .flags = IRQF_NOBALANCING, .name = "smp reschedule IPI" }; static struct irqaction call_function_ipi = { .handler = smp_call_function_interrupt, .flags = IRQF_NOBALANCING, .name = "smp call function IPI" }; #if !defined(CONFIG_GENERIC_CLOCKEVENTS) || defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) static irqreturn_t smp_ipi_timer_interrupt(int irq, void *dev_id); static struct irqaction local_timer_ipi = { .handler = smp_ipi_timer_interrupt, .flags = IRQF_NOBALANCING, .name = "smp local timer IPI" }; #endif /** * init_ipi - Initialise the IPI mechanism */ static void init_ipi(void) { unsigned long flags; u16 tmp16; /* set up the reschedule IPI */ irq_set_chip_and_handler(RESCHEDULE_IPI, &mn10300_ipi_type, handle_percpu_irq); setup_irq(RESCHEDULE_IPI, &reschedule_ipi); set_intr_level(RESCHEDULE_IPI, RESCHEDULE_GxICR_LV); mn10300_ipi_enable(RESCHEDULE_IPI); /* set up the call function IPI */ irq_set_chip_and_handler(CALL_FUNC_SINGLE_IPI, &mn10300_ipi_type, handle_percpu_irq); setup_irq(CALL_FUNC_SINGLE_IPI, &call_function_ipi); set_intr_level(CALL_FUNC_SINGLE_IPI, CALL_FUNCTION_GxICR_LV); mn10300_ipi_enable(CALL_FUNC_SINGLE_IPI); /* set up the local timer IPI */ #if !defined(CONFIG_GENERIC_CLOCKEVENTS) || \ defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) irq_set_chip_and_handler(LOCAL_TIMER_IPI, &mn10300_ipi_type, handle_percpu_irq); setup_irq(LOCAL_TIMER_IPI, &local_timer_ipi); set_intr_level(LOCAL_TIMER_IPI, LOCAL_TIMER_GxICR_LV); mn10300_ipi_enable(LOCAL_TIMER_IPI); #endif #ifdef CONFIG_MN10300_CACHE_ENABLED /* set up the cache flush IPI */ irq_set_chip(FLUSH_CACHE_IPI, &mn10300_ipi_type); flags = arch_local_cli_save(); __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(FLUSH_CACHE_GxICR_LV), mn10300_low_ipi_handler); GxICR(FLUSH_CACHE_IPI) = FLUSH_CACHE_GxICR_LV | GxICR_DETECT; mn10300_ipi_enable(FLUSH_CACHE_IPI); arch_local_irq_restore(flags); #endif /* set up the NMI call function IPI */ irq_set_chip(CALL_FUNCTION_NMI_IPI, &mn10300_ipi_type); flags = arch_local_cli_save(); GxICR(CALL_FUNCTION_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT; tmp16 = GxICR(CALL_FUNCTION_NMI_IPI); arch_local_irq_restore(flags); /* set up the SMP boot IPI */ flags = arch_local_cli_save(); __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(SMP_BOOT_GxICR_LV), mn10300_low_ipi_handler); arch_local_irq_restore(flags); #ifdef CONFIG_KERNEL_DEBUGGER irq_set_chip(DEBUGGER_NMI_IPI, &mn10300_ipi_type); #endif } /** * mn10300_ipi_shutdown - Shut down handling of an IPI * @irq: The IPI to be shut down. */ static void mn10300_ipi_shutdown(unsigned int irq) { unsigned long flags; u16 tmp; flags = arch_local_cli_save(); tmp = GxICR(irq); GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; tmp = GxICR(irq); arch_local_irq_restore(flags); } /** * mn10300_ipi_enable - Enable an IPI * @irq: The IPI to be enabled. */ static void mn10300_ipi_enable(unsigned int irq) { unsigned long flags; u16 tmp; flags = arch_local_cli_save(); tmp = GxICR(irq); GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; tmp = GxICR(irq); arch_local_irq_restore(flags); } static void mn10300_ipi_chip_enable(struct irq_data *d) { mn10300_ipi_enable(d->irq); } /** * mn10300_ipi_disable - Disable an IPI * @irq: The IPI to be disabled. */ static void mn10300_ipi_disable(unsigned int irq) { unsigned long flags; u16 tmp; flags = arch_local_cli_save(); tmp = GxICR(irq); GxICR(irq) = tmp & GxICR_LEVEL; tmp = GxICR(irq); arch_local_irq_restore(flags); } static void mn10300_ipi_chip_disable(struct irq_data *d) { mn10300_ipi_disable(d->irq); } /** * mn10300_ipi_ack - Acknowledge an IPI interrupt in the PIC * @irq: The IPI to be acknowledged. * * Clear the interrupt detection flag for the IPI on the appropriate interrupt * channel in the PIC. */ static void mn10300_ipi_ack(struct irq_data *d) { unsigned int irq = d->irq; unsigned long flags; u16 tmp; flags = arch_local_cli_save(); GxICR_u8(irq) = GxICR_DETECT; tmp = GxICR(irq); arch_local_irq_restore(flags); } /** * mn10300_ipi_nop - Dummy IPI action * @irq: The IPI to be acted upon. */ static void mn10300_ipi_nop(struct irq_data *d) { } /** * send_IPI_mask - Send IPIs to all CPUs in list * @cpumask: The list of CPUs to target. * @irq: The IPI request to be sent. * * Send the specified IPI to all the CPUs in the list, not waiting for them to * finish before returning. The caller is responsible for synchronisation if * that is needed. */ static void send_IPI_mask(const cpumask_t *cpumask, int irq) { int i; u16 tmp; for (i = 0; i < NR_CPUS; i++) { if (cpumask_test_cpu(i, cpumask)) { /* send IPI */ tmp = CROSS_GxICR(irq, i); CROSS_GxICR(irq, i) = tmp | GxICR_REQUEST | GxICR_DETECT; tmp = CROSS_GxICR(irq, i); /* flush write buffer */ } } } /** * send_IPI_self - Send an IPI to this CPU. * @irq: The IPI request to be sent. * * Send the specified IPI to the current CPU. */ void send_IPI_self(int irq) { send_IPI_mask(cpumask_of(smp_processor_id()), irq); } /** * send_IPI_allbutself - Send IPIs to all the other CPUs. * @irq: The IPI request to be sent. * * Send the specified IPI to all CPUs in the system barring the current one, * not waiting for them to finish before returning. The caller is responsible * for synchronisation if that is needed. */ void send_IPI_allbutself(int irq) { cpumask_t cpumask; cpumask_copy(&cpumask, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &cpumask); send_IPI_mask(&cpumask, irq); } void arch_send_call_function_ipi_mask(const struct cpumask *mask) { BUG(); /*send_IPI_mask(mask, CALL_FUNCTION_IPI);*/ } void arch_send_call_function_single_ipi(int cpu) { send_IPI_mask(cpumask_of(cpu), CALL_FUNC_SINGLE_IPI); } /** * smp_send_reschedule - Send reschedule IPI to a CPU * @cpu: The CPU to target. */ void smp_send_reschedule(int cpu) { send_IPI_mask(cpumask_of(cpu), RESCHEDULE_IPI); } /** * smp_nmi_call_function - Send a call function NMI IPI to all CPUs * @func: The function to ask to be run. * @info: The context data to pass to that function. * @wait: If true, wait (atomically) until function is run on all CPUs. * * Send a non-maskable request to all CPUs in the system, requesting them to * run the specified function with the given context data, and, potentially, to * wait for completion of that function on all CPUs. * * Returns 0 if successful, -ETIMEDOUT if we were asked to wait, but hit the * timeout. */ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait) { struct nmi_call_data_struct data; unsigned long flags; unsigned int cnt; int cpus, ret = 0; cpus = num_online_cpus() - 1; if (cpus < 1) return 0; data.func = func; data.info = info; cpumask_copy(&data.started, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &data.started); data.wait = wait; if (wait) data.finished = data.started; spin_lock_irqsave(&smp_nmi_call_lock, flags); nmi_call_data = &data; smp_mb(); /* Send a message to all other CPUs and wait for them to respond */ send_IPI_allbutself(CALL_FUNCTION_NMI_IPI); /* Wait for response */ if (CALL_FUNCTION_NMI_IPI_TIMEOUT > 0) { for (cnt = 0; cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT && !cpumask_empty(&data.started); cnt++) mdelay(1); if (wait && cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT) { for (cnt = 0; cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT && !cpumask_empty(&data.finished); cnt++) mdelay(1); } if (cnt >= CALL_FUNCTION_NMI_IPI_TIMEOUT) ret = -ETIMEDOUT; } else { /* If timeout value is zero, wait until cpumask has been * cleared */ while (!cpumask_empty(&data.started)) barrier(); if (wait) while (!cpumask_empty(&data.finished)) barrier(); } spin_unlock_irqrestore(&smp_nmi_call_lock, flags); return ret; } /** * smp_jump_to_debugger - Make other CPUs enter the debugger by sending an IPI * * Send a non-maskable request to all other CPUs in the system, instructing * them to jump into the debugger. The caller is responsible for checking that * the other CPUs responded to the instruction. * * The caller should make sure that this CPU's debugger IPI is disabled. */ void smp_jump_to_debugger(void) { if (num_online_cpus() > 1) /* Send a message to all other CPUs */ send_IPI_allbutself(DEBUGGER_NMI_IPI); } /** * stop_this_cpu - Callback to stop a CPU. * @unused: Callback context (ignored). */ void stop_this_cpu(void *unused) { static volatile int stopflag; unsigned long flags; #ifdef CONFIG_GDBSTUB /* In case of single stepping smp_send_stop by other CPU, * clear procindebug to avoid deadlock. */ atomic_set(&procindebug[smp_processor_id()], 0); #endif /* CONFIG_GDBSTUB */ flags = arch_local_cli_save(); set_cpu_online(smp_processor_id(), false); while (!stopflag) cpu_relax(); set_cpu_online(smp_processor_id(), true); arch_local_irq_restore(flags); } /** * smp_send_stop - Send a stop request to all CPUs. */ void smp_send_stop(void) { smp_nmi_call_function(stop_this_cpu, NULL, 0); } /** * smp_reschedule_interrupt - Reschedule IPI handler * @irq: The interrupt number. * @dev_id: The device ID. * * Returns IRQ_HANDLED to indicate we handled the interrupt successfully. */ static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id) { scheduler_ipi(); return IRQ_HANDLED; } /** * smp_call_function_interrupt - Call function IPI handler * @irq: The interrupt number. * @dev_id: The device ID. * * Returns IRQ_HANDLED to indicate we handled the interrupt successfully. */ static irqreturn_t smp_call_function_interrupt(int irq, void *dev_id) { /* generic_smp_call_function_interrupt(); */ generic_smp_call_function_single_interrupt(); return IRQ_HANDLED; } /** * smp_nmi_call_function_interrupt - Non-maskable call function IPI handler */ void smp_nmi_call_function_interrupt(void) { smp_call_func_t func = nmi_call_data->func; void *info = nmi_call_data->info; int wait = nmi_call_data->wait; /* Notify the initiating CPU that I've grabbed the data and am about to * execute the function */ smp_mb(); cpumask_clear_cpu(smp_processor_id(), &nmi_call_data->started); (*func)(info); if (wait) { smp_mb(); cpumask_clear_cpu(smp_processor_id(), &nmi_call_data->finished); } } #if !defined(CONFIG_GENERIC_CLOCKEVENTS) || \ defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) /** * smp_ipi_timer_interrupt - Local timer IPI handler * @irq: The interrupt number. * @dev_id: The device ID. * * Returns IRQ_HANDLED to indicate we handled the interrupt successfully. */ static irqreturn_t smp_ipi_timer_interrupt(int irq, void *dev_id) { return local_timer_interrupt(); } #endif void __init smp_init_cpus(void) { int i; for (i = 0; i < NR_CPUS; i++) { set_cpu_possible(i, true); set_cpu_present(i, true); } } /** * smp_cpu_init - Initialise AP in start_secondary. * * For this Application Processor, set up init_mm, initialise FPU and set * interrupt level 0-6 setting. */ static void __init smp_cpu_init(void) { unsigned long flags; int cpu_id = smp_processor_id(); u16 tmp16; if (test_and_set_bit(cpu_id, &cpu_initialized)) { printk(KERN_WARNING "CPU#%d already initialized!\n", cpu_id); for (;;) local_irq_enable(); } printk(KERN_INFO "Initializing CPU#%d\n", cpu_id); atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; BUG_ON(current->mm); enter_lazy_tlb(&init_mm, current); /* Force FPU initialization */ clear_using_fpu(current); GxICR(CALL_FUNC_SINGLE_IPI) = CALL_FUNCTION_GxICR_LV | GxICR_DETECT; mn10300_ipi_enable(CALL_FUNC_SINGLE_IPI); GxICR(LOCAL_TIMER_IPI) = LOCAL_TIMER_GxICR_LV | GxICR_DETECT; mn10300_ipi_enable(LOCAL_TIMER_IPI); GxICR(RESCHEDULE_IPI) = RESCHEDULE_GxICR_LV | GxICR_DETECT; mn10300_ipi_enable(RESCHEDULE_IPI); #ifdef CONFIG_MN10300_CACHE_ENABLED GxICR(FLUSH_CACHE_IPI) = FLUSH_CACHE_GxICR_LV | GxICR_DETECT; mn10300_ipi_enable(FLUSH_CACHE_IPI); #endif mn10300_ipi_shutdown(SMP_BOOT_IRQ); /* Set up the non-maskable call function IPI */ flags = arch_local_cli_save(); GxICR(CALL_FUNCTION_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT; tmp16 = GxICR(CALL_FUNCTION_NMI_IPI); arch_local_irq_restore(flags); } /** * smp_prepare_cpu_init - Initialise CPU in startup_secondary * * Set interrupt level 0-6 setting and init ICR of the kernel debugger. */ void smp_prepare_cpu_init(void) { int loop; /* Set the interrupt vector registers */ IVAR0 = EXCEP_IRQ_LEVEL0; IVAR1 = EXCEP_IRQ_LEVEL1; IVAR2 = EXCEP_IRQ_LEVEL2; IVAR3 = EXCEP_IRQ_LEVEL3; IVAR4 = EXCEP_IRQ_LEVEL4; IVAR5 = EXCEP_IRQ_LEVEL5; IVAR6 = EXCEP_IRQ_LEVEL6; /* Disable all interrupts and set to priority 6 (lowest) */ for (loop = 0; loop < GxICR_NUM_IRQS; loop++) GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT; #ifdef CONFIG_KERNEL_DEBUGGER /* initialise the kernel debugger interrupt */ do { unsigned long flags; u16 tmp16; flags = arch_local_cli_save(); GxICR(DEBUGGER_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT; tmp16 = GxICR(DEBUGGER_NMI_IPI); arch_local_irq_restore(flags); } while (0); #endif } /** * start_secondary - Activate a secondary CPU (AP) * @unused: Thread parameter (ignored). */ int __init start_secondary(void *unused) { smp_cpu_init(); smp_callin(); while (!cpumask_test_cpu(smp_processor_id(), &smp_commenced_mask)) cpu_relax(); local_flush_tlb(); preempt_disable(); smp_online(); #ifdef CONFIG_GENERIC_CLOCKEVENTS init_clockevents(); #endif cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); return 0; } /** * smp_prepare_cpus - Boot up secondary CPUs (APs) * @max_cpus: Maximum number of CPUs to boot. * * Call do_boot_cpu, and boot up APs. */ void __init smp_prepare_cpus(unsigned int max_cpus) { int phy_id; /* Setup boot CPU information */ smp_store_cpu_info(0); smp_tune_scheduling(); init_ipi(); /* If SMP should be disabled, then finish */ if (max_cpus == 0) { printk(KERN_INFO "SMP mode deactivated.\n"); goto smp_done; } /* Boot secondary CPUs (for which phy_id > 0) */ for (phy_id = 0; phy_id < NR_CPUS; phy_id++) { /* Don't boot primary CPU */ if (max_cpus <= cpucount + 1) continue; if (phy_id != 0) do_boot_cpu(phy_id); set_cpu_possible(phy_id, true); smp_show_cpu_info(phy_id); } smp_done: Dprintk("Boot done.\n"); } /** * smp_store_cpu_info - Save a CPU's information * @cpu: The CPU to save for. * * Save boot_cpu_data and jiffy for the specified CPU. */ static void __init smp_store_cpu_info(int cpu) { struct mn10300_cpuinfo *ci = &cpu_data[cpu]; *ci = boot_cpu_data; ci->loops_per_jiffy = loops_per_jiffy; ci->type = CPUREV; } /** * smp_tune_scheduling - Set time slice value * * Nothing to do here. */ static void __init smp_tune_scheduling(void) { } /** * do_boot_cpu: Boot up one CPU * @phy_id: Physical ID of CPU to boot. * * Send an IPI to a secondary CPU to boot it. Returns 0 on success, 1 * otherwise. */ static int __init do_boot_cpu(int phy_id) { struct task_struct *idle; unsigned long send_status, callin_status; int timeout, cpu_id; send_status = GxICR_REQUEST; callin_status = 0; timeout = 0; cpu_id = phy_id; cpucount++; /* Create idle thread for this CPU */ idle = fork_idle(cpu_id); if (IS_ERR(idle)) panic("Failed fork for CPU#%d.", cpu_id); idle->thread.pc = (unsigned long)start_secondary; printk(KERN_NOTICE "Booting CPU#%d\n", cpu_id); start_stack[cpu_id - 1] = idle->thread.sp; task_thread_info(idle)->cpu = cpu_id; /* Send boot IPI to AP */ send_IPI_mask(cpumask_of(phy_id), SMP_BOOT_IRQ); Dprintk("Waiting for send to finish...\n"); /* Wait for AP's IPI receive in 100[ms] */ do { udelay(1000); send_status = CROSS_GxICR(SMP_BOOT_IRQ, phy_id) & GxICR_REQUEST; } while (send_status == GxICR_REQUEST && timeout++ < 100); Dprintk("Waiting for cpu_callin_map.\n"); if (send_status == 0) { /* Allow AP to start initializing */ cpumask_set_cpu(cpu_id, &cpu_callout_map); /* Wait for setting cpu_callin_map */ timeout = 0; do { udelay(1000); callin_status = cpumask_test_cpu(cpu_id, &cpu_callin_map); } while (callin_status == 0 && timeout++ < 5000); if (callin_status == 0) Dprintk("Not responding.\n"); } else { printk(KERN_WARNING "IPI not delivered.\n"); } if (send_status == GxICR_REQUEST || callin_status == 0) { cpumask_clear_cpu(cpu_id, &cpu_callout_map); cpumask_clear_cpu(cpu_id, &cpu_callin_map); cpumask_clear_cpu(cpu_id, &cpu_initialized); cpucount--; return 1; } return 0; } /** * smp_show_cpu_info - Show SMP CPU information * @cpu: The CPU of interest. */ static void __init smp_show_cpu_info(int cpu) { struct mn10300_cpuinfo *ci = &cpu_data[cpu]; printk(KERN_INFO "CPU#%d : ioclk speed: %lu.%02luMHz : bogomips : %lu.%02lu\n", cpu, MN10300_IOCLK / 1000000, (MN10300_IOCLK / 10000) % 100, ci->loops_per_jiffy / (500000 / HZ), (ci->loops_per_jiffy / (5000 / HZ)) % 100); } /** * smp_callin - Set cpu_callin_map of the current CPU ID */ static void __init smp_callin(void) { unsigned long timeout; int cpu; cpu = smp_processor_id(); timeout = jiffies + (2 * HZ); if (cpumask_test_cpu(cpu, &cpu_callin_map)) { printk(KERN_ERR "CPU#%d already present.\n", cpu); BUG(); } Dprintk("CPU#%d waiting for CALLOUT\n", cpu); /* Wait for AP startup 2s total */ while (time_before(jiffies, timeout)) { if (cpumask_test_cpu(cpu, &cpu_callout_map)) break; cpu_relax(); } if (!time_before(jiffies, timeout)) { printk(KERN_ERR "BUG: CPU#%d started up but did not get a callout!\n", cpu); BUG(); } #ifdef CONFIG_CALIBRATE_DELAY calibrate_delay(); /* Get our bogomips */ #endif /* Save our processor parameters */ smp_store_cpu_info(cpu); /* Allow the boot processor to continue */ cpumask_set_cpu(cpu, &cpu_callin_map); } /** * smp_online - Set cpu_online_mask */ static void __init smp_online(void) { int cpu; cpu = smp_processor_id(); notify_cpu_starting(cpu); set_cpu_online(cpu, true); local_irq_enable(); } /** * smp_cpus_done - * @max_cpus: Maximum CPU count. * * Do nothing. */ void __init smp_cpus_done(unsigned int max_cpus) { } /* * smp_prepare_boot_cpu - Set up stuff for the boot processor. * * Set up the cpu_online_mask, cpu_callout_map and cpu_callin_map of the boot * processor (CPU 0). */ void smp_prepare_boot_cpu(void) { cpumask_set_cpu(0, &cpu_callout_map); cpumask_set_cpu(0, &cpu_callin_map); current_thread_info()->cpu = 0; } /* * initialize_secondary - Initialise a secondary CPU (Application Processor). * * Set SP register and jump to thread's PC address. */ void initialize_secondary(void) { asm volatile ( "mov %0,sp \n" "jmp (%1) \n" : : "a"(current->thread.sp), "a"(current->thread.pc)); } /** * __cpu_up - Set smp_commenced_mask for the nominated CPU * @cpu: The target CPU. */ int __cpu_up(unsigned int cpu, struct task_struct *tidle) { int timeout; #ifdef CONFIG_HOTPLUG_CPU if (sleep_mode[cpu]) run_wakeup_cpu(cpu); #endif /* CONFIG_HOTPLUG_CPU */ cpumask_set_cpu(cpu, &smp_commenced_mask); /* Wait 5s total for a response */ for (timeout = 0 ; timeout < 5000 ; timeout++) { if (cpu_online(cpu)) break; udelay(1000); } BUG_ON(!cpu_online(cpu)); return 0; } /** * setup_profiling_timer - Set up the profiling timer * @multiplier - The frequency multiplier to use * * The frequency of the profiling timer can be changed by writing a multiplier * value into /proc/profile. */ int setup_profiling_timer(unsigned int multiplier) { return -EINVAL; } /* * CPU hotplug routines */ #ifdef CONFIG_HOTPLUG_CPU static DEFINE_PER_CPU(struct cpu, cpu_devices); static int __init topology_init(void) { int cpu, ret; for_each_cpu(cpu) { ret = register_cpu(&per_cpu(cpu_devices, cpu), cpu, NULL); if (ret) printk(KERN_WARNING "topology_init: register_cpu %d failed (%d)\n", cpu, ret); } return 0; } subsys_initcall(topology_init); int __cpu_disable(void) { int cpu = smp_processor_id(); if (cpu == 0) return -EBUSY; migrate_irqs(); cpumask_clear_cpu(cpu, &mm_cpumask(current->active_mm)); return 0; } void __cpu_die(unsigned int cpu) { run_sleep_cpu(cpu); } #ifdef CONFIG_MN10300_CACHE_ENABLED static inline void hotplug_cpu_disable_cache(void) { int tmp; asm volatile( " movhu (%1),%0 \n" " and %2,%0 \n" " movhu %0,(%1) \n" "1: movhu (%1),%0 \n" " btst %3,%0 \n" " bne 1b \n" : "=&r"(tmp) : "a"(&CHCTR), "i"(~(CHCTR_ICEN | CHCTR_DCEN)), "i"(CHCTR_ICBUSY | CHCTR_DCBUSY) : "memory", "cc"); } static inline void hotplug_cpu_enable_cache(void) { int tmp; asm volatile( "movhu (%1),%0 \n" "or %2,%0 \n" "movhu %0,(%1) \n" : "=&r"(tmp) : "a"(&CHCTR), "i"(CHCTR_ICEN | CHCTR_DCEN) : "memory", "cc"); } static inline void hotplug_cpu_invalidate_cache(void) { int tmp; asm volatile ( "movhu (%1),%0 \n" "or %2,%0 \n" "movhu %0,(%1) \n" : "=&r"(tmp) : "a"(&CHCTR), "i"(CHCTR_ICINV | CHCTR_DCINV) : "cc"); } #else /* CONFIG_MN10300_CACHE_ENABLED */ #define hotplug_cpu_disable_cache() do {} while (0) #define hotplug_cpu_enable_cache() do {} while (0) #define hotplug_cpu_invalidate_cache() do {} while (0) #endif /* CONFIG_MN10300_CACHE_ENABLED */ /** * hotplug_cpu_nmi_call_function - Call a function on other CPUs for hotplug * @cpumask: List of target CPUs. * @func: The function to call on those CPUs. * @info: The context data for the function to be called. * @wait: Whether to wait for the calls to complete. * * Non-maskably call a function on another CPU for hotplug purposes. * * This function must be called with maskable interrupts disabled. */ static int hotplug_cpu_nmi_call_function(cpumask_t cpumask, smp_call_func_t func, void *info, int wait) { /* * The address and the size of nmi_call_func_mask_data * need to be aligned on L1_CACHE_BYTES. */ static struct nmi_call_data_struct nmi_call_func_mask_data __cacheline_aligned; unsigned long start, end; start = (unsigned long)&nmi_call_func_mask_data; end = start + sizeof(struct nmi_call_data_struct); nmi_call_func_mask_data.func = func; nmi_call_func_mask_data.info = info; nmi_call_func_mask_data.started = cpumask; nmi_call_func_mask_data.wait = wait; if (wait) nmi_call_func_mask_data.finished = cpumask; spin_lock(&smp_nmi_call_lock); nmi_call_data = &nmi_call_func_mask_data; mn10300_local_dcache_flush_range(start, end); smp_wmb(); send_IPI_mask(cpumask, CALL_FUNCTION_NMI_IPI); do { mn10300_local_dcache_inv_range(start, end); barrier(); } while (!cpumask_empty(&nmi_call_func_mask_data.started)); if (wait) { do { mn10300_local_dcache_inv_range(start, end); barrier(); } while (!cpumask_empty(&nmi_call_func_mask_data.finished)); } spin_unlock(&smp_nmi_call_lock); return 0; } static void restart_wakeup_cpu(void) { unsigned int cpu = smp_processor_id(); cpumask_set_cpu(cpu, &cpu_callin_map); local_flush_tlb(); set_cpu_online(cpu, true); smp_wmb(); } static void prepare_sleep_cpu(void *unused) { sleep_mode[smp_processor_id()] = 1; smp_mb(); mn10300_local_dcache_flush_inv(); hotplug_cpu_disable_cache(); hotplug_cpu_invalidate_cache(); } /* when this function called, IE=0, NMID=0. */ static void sleep_cpu(void *unused) { unsigned int cpu_id = smp_processor_id(); /* * CALL_FUNCTION_NMI_IPI for wakeup_cpu() shall not be requested, * before this cpu goes in SLEEP mode. */ do { smp_mb(); __sleep_cpu(); } while (sleep_mode[cpu_id]); restart_wakeup_cpu(); } static void run_sleep_cpu(unsigned int cpu) { unsigned long flags; cpumask_t cpumask; cpumask_copy(&cpumask, &cpumask_of(cpu)); flags = arch_local_cli_save(); hotplug_cpu_nmi_call_function(cpumask, prepare_sleep_cpu, NULL, 1); hotplug_cpu_nmi_call_function(cpumask, sleep_cpu, NULL, 0); udelay(1); /* delay for the cpu to sleep. */ arch_local_irq_restore(flags); } static void wakeup_cpu(void) { hotplug_cpu_invalidate_cache(); hotplug_cpu_enable_cache(); smp_mb(); sleep_mode[smp_processor_id()] = 0; } static void run_wakeup_cpu(unsigned int cpu) { unsigned long flags; flags = arch_local_cli_save(); #if NR_CPUS == 2 mn10300_local_dcache_flush_inv(); #else /* * Before waking up the cpu, * all online cpus should stop and flush D-Cache for global data. */ #error not support NR_CPUS > 2, when CONFIG_HOTPLUG_CPU=y. #endif hotplug_cpu_nmi_call_function(cpumask_of(cpu), wakeup_cpu, NULL, 1); arch_local_irq_restore(flags); } #endif /* CONFIG_HOTPLUG_CPU */