linux/arch/arm/plat-versatile/platsmp.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/plat-versatile/platsmp.c
   3 *
   4 *  Copyright (C) 2002 ARM Ltd.
   5 *  All Rights Reserved
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11#include <linux/init.h>
  12#include <linux/errno.h>
  13#include <linux/delay.h>
  14#include <linux/device.h>
  15#include <linux/jiffies.h>
  16#include <linux/smp.h>
  17
  18#include <asm/cacheflush.h>
  19#include <asm/hardware/gic.h>
  20
  21/*
  22 * control for which core is the next to come out of the secondary
  23 * boot "holding pen"
  24 */
  25volatile int __cpuinitdata pen_release = -1;
  26
  27/*
  28 * Write pen_release in a way that is guaranteed to be visible to all
  29 * observers, irrespective of whether they're taking part in coherency
  30 * or not.  This is necessary for the hotplug code to work reliably.
  31 */
  32static void __cpuinit write_pen_release(int val)
  33{
  34        pen_release = val;
  35        smp_wmb();
  36        __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
  37        outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
  38}
  39
  40static DEFINE_SPINLOCK(boot_lock);
  41
  42void __cpuinit platform_secondary_init(unsigned int cpu)
  43{
  44        /*
  45         * if any interrupts are already enabled for the primary
  46         * core (e.g. timer irq), then they will not have been enabled
  47         * for us: do so
  48         */
  49        gic_secondary_init(0);
  50
  51        /*
  52         * let the primary processor know we're out of the
  53         * pen, then head off into the C entry point
  54         */
  55        write_pen_release(-1);
  56
  57        /*
  58         * Synchronise with the boot thread.
  59         */
  60        spin_lock(&boot_lock);
  61        spin_unlock(&boot_lock);
  62}
  63
  64int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
  65{
  66        unsigned long timeout;
  67
  68        /*
  69         * Set synchronisation state between this boot processor
  70         * and the secondary one
  71         */
  72        spin_lock(&boot_lock);
  73
  74        /*
  75         * This is really belt and braces; we hold unintended secondary
  76         * CPUs in the holding pen until we're ready for them.  However,
  77         * since we haven't sent them a soft interrupt, they shouldn't
  78         * be there.
  79         */
  80        write_pen_release(cpu);
  81
  82        /*
  83         * Send the secondary CPU a soft interrupt, thereby causing
  84         * the boot monitor to read the system wide flags register,
  85         * and branch to the address found there.
  86         */
  87        gic_raise_softirq(cpumask_of(cpu), 1);
  88
  89        timeout = jiffies + (1 * HZ);
  90        while (time_before(jiffies, timeout)) {
  91                smp_rmb();
  92                if (pen_release == -1)
  93                        break;
  94
  95                udelay(10);
  96        }
  97
  98        /*
  99         * now the secondary core is starting up let it run its
 100         * calibrations, then wait for it to finish
 101         */
 102        spin_unlock(&boot_lock);
 103
 104        return pen_release != -1 ? -ENOSYS : 0;
 105}
 106