linux/arch/hexagon/kernel/smp.c
<<
>>
Prefs
   1/*
   2 * SMP support for Hexagon
   3 *
   4 * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 and
   8 * only version 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18 * 02110-1301, USA.
  19 */
  20
  21#include <linux/err.h>
  22#include <linux/errno.h>
  23#include <linux/kernel.h>
  24#include <linux/init.h>
  25#include <linux/interrupt.h>
  26#include <linux/module.h>
  27#include <linux/percpu.h>
  28#include <linux/sched/mm.h>
  29#include <linux/smp.h>
  30#include <linux/spinlock.h>
  31#include <linux/cpu.h>
  32#include <linux/mm_types.h>
  33
  34#include <asm/time.h>    /*  timer_interrupt  */
  35#include <asm/hexagon_vm.h>
  36
  37#define BASE_IPI_IRQ 26
  38
  39/*
  40 * cpu_possible_mask needs to be filled out prior to setup_per_cpu_areas
  41 * (which is prior to any of our smp_prepare_cpu crap), in order to set
  42 * up the...  per_cpu areas.
  43 */
  44
  45struct ipi_data {
  46        unsigned long bits;
  47};
  48
  49static DEFINE_PER_CPU(struct ipi_data, ipi_data);
  50
  51static inline void __handle_ipi(unsigned long *ops, struct ipi_data *ipi,
  52                                int cpu)
  53{
  54        unsigned long msg = 0;
  55        do {
  56                msg = find_next_bit(ops, BITS_PER_LONG, msg+1);
  57
  58                switch (msg) {
  59
  60                case IPI_TIMER:
  61                        ipi_timer();
  62                        break;
  63
  64                case IPI_CALL_FUNC:
  65                        generic_smp_call_function_interrupt();
  66                        break;
  67
  68                case IPI_CPU_STOP:
  69                        /*
  70                         * call vmstop()
  71                         */
  72                        __vmstop();
  73                        break;
  74
  75                case IPI_RESCHEDULE:
  76                        scheduler_ipi();
  77                        break;
  78                }
  79        } while (msg < BITS_PER_LONG);
  80}
  81
  82/*  Used for IPI call from other CPU's to unmask int  */
  83void smp_vm_unmask_irq(void *info)
  84{
  85        __vmintop_locen((long) info);
  86}
  87
  88
  89/*
  90 * This is based on Alpha's IPI stuff.
  91 * Supposed to take (int, void*) as args now.
  92 * Specifically, first arg is irq, second is the irq_desc.
  93 */
  94
  95irqreturn_t handle_ipi(int irq, void *desc)
  96{
  97        int cpu = smp_processor_id();
  98        struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
  99        unsigned long ops;
 100
 101        while ((ops = xchg(&ipi->bits, 0)) != 0)
 102                __handle_ipi(&ops, ipi, cpu);
 103        return IRQ_HANDLED;
 104}
 105
 106void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg)
 107{
 108        unsigned long flags;
 109        unsigned long cpu;
 110        unsigned long retval;
 111
 112        local_irq_save(flags);
 113
 114        for_each_cpu(cpu, cpumask) {
 115                struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
 116
 117                set_bit(msg, &ipi->bits);
 118                /*  Possible barrier here  */
 119                retval = __vmintop_post(BASE_IPI_IRQ+cpu);
 120
 121                if (retval != 0) {
 122                        printk(KERN_ERR "interrupt %ld not configured?\n",
 123                                BASE_IPI_IRQ+cpu);
 124                }
 125        }
 126
 127        local_irq_restore(flags);
 128}
 129
 130static struct irqaction ipi_intdesc = {
 131        .handler = handle_ipi,
 132        .flags = IRQF_TRIGGER_RISING,
 133        .name = "ipi_handler"
 134};
 135
 136void __init smp_prepare_boot_cpu(void)
 137{
 138}
 139
 140/*
 141 * interrupts should already be disabled from the VM
 142 * SP should already be correct; need to set THREADINFO_REG
 143 * to point to current thread info
 144 */
 145
 146void start_secondary(void)
 147{
 148        unsigned int cpu;
 149        unsigned long thread_ptr;
 150
 151        /*  Calculate thread_info pointer from stack pointer  */
 152        __asm__ __volatile__(
 153                "%0 = SP;\n"
 154                : "=r" (thread_ptr)
 155        );
 156
 157        thread_ptr = thread_ptr & ~(THREAD_SIZE-1);
 158
 159        __asm__ __volatile__(
 160                QUOTED_THREADINFO_REG " = %0;\n"
 161                :
 162                : "r" (thread_ptr)
 163        );
 164
 165        /*  Set the memory struct  */
 166        mmgrab(&init_mm);
 167        current->active_mm = &init_mm;
 168
 169        cpu = smp_processor_id();
 170
 171        setup_irq(BASE_IPI_IRQ + cpu, &ipi_intdesc);
 172
 173        /*  Register the clock_event dummy  */
 174        setup_percpu_clockdev();
 175
 176        printk(KERN_INFO "%s cpu %d\n", __func__, current_thread_info()->cpu);
 177
 178        notify_cpu_starting(cpu);
 179
 180        set_cpu_online(cpu, true);
 181
 182        local_irq_enable();
 183
 184        cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
 185}
 186
 187
 188/*
 189 * called once for each present cpu
 190 * apparently starts up the CPU and then
 191 * maintains control until "cpu_online(cpu)" is set.
 192 */
 193
 194int __cpu_up(unsigned int cpu, struct task_struct *idle)
 195{
 196        struct thread_info *thread = (struct thread_info *)idle->stack;
 197        void *stack_start;
 198
 199        thread->cpu = cpu;
 200
 201        /*  Boot to the head.  */
 202        stack_start =  ((void *) thread) + THREAD_SIZE;
 203        __vmstart(start_secondary, stack_start);
 204
 205        while (!cpu_online(cpu))
 206                barrier();
 207
 208        return 0;
 209}
 210
 211void __init smp_cpus_done(unsigned int max_cpus)
 212{
 213}
 214
 215void __init smp_prepare_cpus(unsigned int max_cpus)
 216{
 217        int i;
 218
 219        /*
 220         * should eventually have some sort of machine
 221         * descriptor that has this stuff
 222         */
 223
 224        /*  Right now, let's just fake it. */
 225        for (i = 0; i < max_cpus; i++)
 226                set_cpu_present(i, true);
 227
 228        /*  Also need to register the interrupts for IPI  */
 229        if (max_cpus > 1)
 230                setup_irq(BASE_IPI_IRQ, &ipi_intdesc);
 231}
 232
 233void smp_send_reschedule(int cpu)
 234{
 235        send_ipi(cpumask_of(cpu), IPI_RESCHEDULE);
 236}
 237
 238void smp_send_stop(void)
 239{
 240        struct cpumask targets;
 241        cpumask_copy(&targets, cpu_online_mask);
 242        cpumask_clear_cpu(smp_processor_id(), &targets);
 243        send_ipi(&targets, IPI_CPU_STOP);
 244}
 245
 246void arch_send_call_function_single_ipi(int cpu)
 247{
 248        send_ipi(cpumask_of(cpu), IPI_CALL_FUNC);
 249}
 250
 251void arch_send_call_function_ipi_mask(const struct cpumask *mask)
 252{
 253        send_ipi(mask, IPI_CALL_FUNC);
 254}
 255
 256int setup_profiling_timer(unsigned int multiplier)
 257{
 258        return -EINVAL;
 259}
 260
 261void smp_start_cpus(void)
 262{
 263        int i;
 264
 265        for (i = 0; i < NR_CPUS; i++)
 266                set_cpu_possible(i, true);
 267}
 268