On Thu, Sep 15, 2011 at 2:19 AM, Richard Kuo <rkuo@xxxxxxxxxxxxxx> wrote: > Signed-off-by: Richard Kuo <rkuo@xxxxxxxxxxxxxx> > Signed-off-by: Linas Vepstas <linas@xxxxxxxxxxxxxx> > Acked-by: Arnd Bergmann <arnd@xxxxxxxx> > --- > arch/hexagon/include/asm/smp.h | 44 +++++++ > arch/hexagon/kernel/smp.c | 280 ++++++++++++++++++++++++++++++++++++++++ > arch/hexagon/kernel/topology.c | 52 ++++++++ > 3 files changed, 376 insertions(+), 0 deletions(-) > create mode 100644 arch/hexagon/include/asm/smp.h > create mode 100644 arch/hexagon/kernel/smp.c > create mode 100644 arch/hexagon/kernel/topology.c > > diff --git a/arch/hexagon/include/asm/smp.h b/arch/hexagon/include/asm/smp.h > new file mode 100644 > index 0000000..87c869a > --- /dev/null > +++ b/arch/hexagon/include/asm/smp.h > @@ -0,0 +1,44 @@ > +/* > + * SMP definitions for the Hexagon architecture > + * > + * Copyright (c) 2010-2011, Code Aurora Forum. 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 and > + * only 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. > + * > + * 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., 51 Franklin Street, Fifth Floor, Boston, MA > + * 02110-1301, USA. > + */ > + > +#ifndef __ASM_SMP_H > +#define __ASM_SMP_H > + > +#include <linux/cpumask.h> > + > +#define raw_smp_processor_id() (current_thread_info()->cpu) > + > +enum ipi_message_type { > + IPI_NOP = 0, > + IPI_RESCHEDULE = 1, > + IPI_CALL_FUNC, > + IPI_CALL_FUNC_SINGLE, > + IPI_CPU_STOP, > + IPI_TIMER, > +}; > + > +extern void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg); > +extern void smp_start_cpus(void); > +extern void arch_send_call_function_single_ipi(int cpu); > +extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); > + > +extern void smp_vm_unmask_irq(void *info); > + > +#endif > diff --git a/arch/hexagon/kernel/smp.c b/arch/hexagon/kernel/smp.c > new file mode 100644 > index 0000000..96bf6c2 > --- /dev/null > +++ b/arch/hexagon/kernel/smp.c > @@ -0,0 +1,280 @@ > +/* > + * SMP support for Hexagon > + * > + * Copyright (c) 2010-2011, Code Aurora Forum. 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 and > + * only 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. > + * > + * 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., 51 Franklin Street, Fifth Floor, Boston, MA > + * 02110-1301, USA. > + */ > + > +#include <linux/err.h> > +#include <linux/errno.h> > +#include <linux/kernel.h> > +#include <linux/init.h> > +#include <linux/interrupt.h> > +#include <linux/module.h> > +#include <linux/percpu.h> > +#include <linux/sched.h> > +#include <linux/smp.h> > +#include <linux/spinlock.h> > + > +#include <asm/system.h> /* xchg */ > +#include <asm/time.h> /* timer_interrupt */ > +#include <asm/hexagon_vm.h> > + > +#define BASE_IPI_IRQ 26 > + > +/* > + * cpu_possible_map needs to be filled out prior to setup_per_cpu_areas > + * (which is prior to any of our smp_prepare_cpu crap), in order to set > + * up the... per_cpu areas. > + */ > + > +struct ipi_data { > + unsigned long bits; > +}; > + > +static DEFINE_PER_CPU(struct ipi_data, ipi_data); > + > +static inline void __handle_ipi(unsigned long *ops, struct ipi_data *ipi, > + int cpu) > +{ > + unsigned long msg = 0; > + do { > + msg = find_next_bit(ops, BITS_PER_LONG, msg+1); > + > + switch (msg) { > + > + case IPI_TIMER: > + ipi_timer(); > + break; > + > + case IPI_CALL_FUNC: > + generic_smp_call_function_interrupt(); > + break; > + > + case IPI_CALL_FUNC_SINGLE: > + generic_smp_call_function_single_interrupt(); > + break; > + > + case IPI_CPU_STOP: > + /* > + * call vmstop() > + */ > + __vmstop(); > + break; > + > + case IPI_RESCHEDULE: > + /* > + * need to revisit our exit path; see comments for > + * scheduler_ipi() > + */ > + scheduler_ipi(); > + break; > + } > + } while (msg < BITS_PER_LONG); > +} > + > +/* Used for IPI call from other CPU's to unmask int */ > +void smp_vm_unmask_irq(void *info) > +{ > + __vmintop_locen((long) info); > +} > + > + > +/* > + * This is based on Alpha's IPI stuff. > + * Supposed to take (int, void*) as args now. > + * Specifically, first arg is irq, second is the irq_desc. > + */ > + > +irqreturn_t handle_ipi(int irq, void *desc) > +{ > + int cpu = smp_processor_id(); > + struct ipi_data *ipi = &per_cpu(ipi_data, cpu); > + unsigned long ops; > + > + while ((ops = xchg(&ipi->bits, 0)) != 0) > + __handle_ipi(&ops, ipi, cpu); > + return IRQ_HANDLED; > +} > + > +void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg) > +{ > + unsigned long flags; > + unsigned long cpu; > + unsigned long retval; > + > + local_irq_save(flags); > + > + for_each_cpu(cpu, cpumask) { > + struct ipi_data *ipi = &per_cpu(ipi_data, cpu); > + > + set_bit(msg, &ipi->bits); > + /* Possible barrier here */ > + retval = __vmintop_post(BASE_IPI_IRQ+cpu); > + > + if (retval != 0) { > + printk(KERN_ERR "interrupt %ld not configured?\n", > + BASE_IPI_IRQ+cpu); > + } > + } > + > + local_irq_restore(flags); > +} > + > +static struct irqaction ipi_intdesc = { > + .handler = handle_ipi, > + .flags = IRQF_DISABLED | IRQF_TRIGGER_RISING, > + .name = "ipi_handler" > +}; > + > +void __init smp_prepare_boot_cpu(void) > +{ > +} > + > +/* > + * interrupts should already be disabled from the VM > + * SP should already be correct; need to set THREADINFO_REG > + * to point to current thread info > + */ > + > +void __cpuinit start_secondary(void) > +{ > + unsigned int cpu; > + unsigned long thread_ptr; > + > + /* Calculate thread_info pointer from stack pointer */ > + __asm__ __volatile__( > + "%0 = SP;\n" > + : "=r" (thread_ptr) > + ); > + > + thread_ptr = thread_ptr & ~(THREAD_SIZE-1); > + > + __asm__ __volatile__( > + QUOTED_THREADINFO_REG " = %0;\n" > + : > + : "r" (thread_ptr) > + ); > + > + /* Set the memory struct */ > + atomic_inc(&init_mm.mm_count); > + current->active_mm = &init_mm; > + > + cpu = smp_processor_id(); > + > + setup_irq(BASE_IPI_IRQ + cpu, &ipi_intdesc); > + > + /* Register the clock_event dummy */ > + setup_percpu_clockdev(); > + > + printk(KERN_INFO "%s cpu %d\n", __func__, current_thread_info()->cpu); > + > + set_cpu_online(cpu, true); > + while (!cpumask_test_cpu(cpu, cpu_active_mask)) > + cpu_relax(); > + local_irq_enable(); > + > + cpu_idle(); > +} > + > + > +/* > + * called once for each present cpu > + * apparently starts up the CPU and then > + * maintains control until "cpu_online(cpu)" is set. > + */ > + __cpu_up only called once? > +int __cpuinit __cpu_up(unsigned int cpu) > +{ > + struct task_struct *idle; > + struct thread_info *thread; > + void *stack_start; > + > + /* Create new init task for the CPU */ > + idle = fork_idle(cpu); > + if (IS_ERR(idle)) > + panic(KERN_ERR "fork_idle failed\n"); > + > + thread = (struct thread_info *)idle->stack; > + thread->cpu = cpu; > + > + /* Boot to the head. */ > + stack_start = ((void *) thread) + THREAD_SIZE; > + __vmstart(start_secondary, stack_start); > + > + while (!cpu_isset(cpu, cpu_online_map)) > + barrier(); > + > + return 0; > +} > + > +void __init smp_cpus_done(unsigned int max_cpus) > +{ > +} > + > +void __init smp_prepare_cpus(unsigned int max_cpus) > +{ > + int i; > + > + /* > + * should eventually have some sort of machine > + * descriptor that has this stuff > + */ > + > + /* Right now, let's just fake it. */ > + for (i = 0; i < max_cpus; i++) > + cpu_set(i, cpu_present_map); > + > + /* Also need to register the interrupts for IPI */ > + if (max_cpus > 1) > + setup_irq(BASE_IPI_IRQ, &ipi_intdesc); > +} > + > +void smp_send_reschedule(int cpu) > +{ > + send_ipi(cpumask_of(cpu), IPI_RESCHEDULE); > +} > + > +void smp_send_stop(void) > +{ > + struct cpumask targets; > + cpumask_copy(&targets, cpu_online_mask); > + cpumask_clear_cpu(smp_processor_id(), &targets); > + send_ipi(&targets, IPI_CPU_STOP); > +} > + > +void arch_send_call_function_single_ipi(int cpu) > +{ > + send_ipi(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); > +} > + > +void arch_send_call_function_ipi_mask(const struct cpumask *mask) > +{ > + send_ipi(mask, IPI_CALL_FUNC); > +} > + > +int setup_profiling_timer(unsigned int multiplier) > +{ > + return -EINVAL; > +} > + > +void smp_start_cpus(void) > +{ > + int i; > + > + for (i = 0; i < NR_CPUS; i++) > + cpu_set(i, cpu_possible_map); > +} > diff --git a/arch/hexagon/kernel/topology.c b/arch/hexagon/kernel/topology.c > new file mode 100644 > index 0000000..ba44751 > --- /dev/null > +++ b/arch/hexagon/kernel/topology.c > @@ -0,0 +1,52 @@ > +/* > + * CPU topology for Hexagon > + * > + * Copyright (c) 2010-2011, Code Aurora Forum. 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 and > + * only 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. > + * > + * 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., 51 Franklin Street, Fifth Floor, Boston, MA > + * 02110-1301, USA. > + */ > + > +#include <linux/cpu.h> > +#include <linux/cpumask.h> > +#include <linux/init.h> > +#include <linux/node.h> > +#include <linux/nodemask.h> > +#include <linux/percpu.h> > + > +/* Swiped from MIPS. */ > + > +static DEFINE_PER_CPU(struct cpu, cpu_devices); > + > +static int __init topology_init(void) > +{ > + int i, ret; > + > + for_each_present_cpu(i) { > + > + /* > + * register_cpu takes a per_cpu pointer and > + * just points it at another per_cpu struct... > + */ > + > + ret = register_cpu(&per_cpu(cpu_devices, i), i); > + if (ret) > + printk(KERN_WARNING "topology_init: register_cpu %d " > + "failed (%d)\n", i, ret); > + } > + > + return 0; > +} > + > +subsys_initcall(topology_init); > -- > 1.7.1 > > > -- > > Sent by an employee of the Qualcomm Innovation Center, Inc. > The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum. > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > -- To unsubscribe from this list: send the line "unsubscribe linux-arch" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html