linux/arch/arm/mach-realview/platsmp.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-realview/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#include <linux/io.h>
  18
  19#include <asm/cacheflush.h>
  20#include <mach/hardware.h>
  21#include <asm/mach-types.h>
  22#include <asm/localtimer.h>
  23#include <asm/unified.h>
  24
  25#include <mach/board-eb.h>
  26#include <mach/board-pb11mp.h>
  27#include <mach/board-pbx.h>
  28#include <asm/smp_scu.h>
  29
  30#include "core.h"
  31
  32extern void realview_secondary_startup(void);
  33
  34/*
  35 * control for which core is the next to come out of the secondary
  36 * boot "holding pen"
  37 */
  38volatile int __cpuinitdata pen_release = -1;
  39
  40static void __iomem *scu_base_addr(void)
  41{
  42        if (machine_is_realview_eb_mp())
  43                return __io_address(REALVIEW_EB11MP_SCU_BASE);
  44        else if (machine_is_realview_pb11mp())
  45                return __io_address(REALVIEW_TC11MP_SCU_BASE);
  46        else if (machine_is_realview_pbx() &&
  47                 (core_tile_pbx11mp() || core_tile_pbxa9mp()))
  48                return __io_address(REALVIEW_PBX_TILE_SCU_BASE);
  49        else
  50                return (void __iomem *)0;
  51}
  52
  53static inline unsigned int get_core_count(void)
  54{
  55        void __iomem *scu_base = scu_base_addr();
  56        if (scu_base)
  57                return scu_get_core_count(scu_base);
  58        return 1;
  59}
  60
  61static DEFINE_SPINLOCK(boot_lock);
  62
  63void __cpuinit platform_secondary_init(unsigned int cpu)
  64{
  65        trace_hardirqs_off();
  66
  67        /*
  68         * if any interrupts are already enabled for the primary
  69         * core (e.g. timer irq), then they will not have been enabled
  70         * for us: do so
  71         */
  72        gic_cpu_init(0, gic_cpu_base_addr);
  73
  74        /*
  75         * let the primary processor know we're out of the
  76         * pen, then head off into the C entry point
  77         */
  78        pen_release = -1;
  79        smp_wmb();
  80
  81        /*
  82         * Synchronise with the boot thread.
  83         */
  84        spin_lock(&boot_lock);
  85        spin_unlock(&boot_lock);
  86}
  87
  88int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
  89{
  90        unsigned long timeout;
  91
  92        /*
  93         * set synchronisation state between this boot processor
  94         * and the secondary one
  95         */
  96        spin_lock(&boot_lock);
  97
  98        /*
  99         * The secondary processor is waiting to be released from
 100         * the holding pen - release it, then wait for it to flag
 101         * that it has been released by resetting pen_release.
 102         *
 103         * Note that "pen_release" is the hardware CPU ID, whereas
 104         * "cpu" is Linux's internal ID.
 105         */
 106        pen_release = cpu;
 107        flush_cache_all();
 108
 109        /*
 110         * XXX
 111         *
 112         * This is a later addition to the booting protocol: the
 113         * bootMonitor now puts secondary cores into WFI, so
 114         * poke_milo() no longer gets the cores moving; we need
 115         * to send a soft interrupt to wake the secondary core.
 116         * Use smp_cross_call() for this, since there's little
 117         * point duplicating the code here
 118         */
 119        smp_cross_call(cpumask_of(cpu));
 120
 121        timeout = jiffies + (1 * HZ);
 122        while (time_before(jiffies, timeout)) {
 123                smp_rmb();
 124                if (pen_release == -1)
 125                        break;
 126
 127                udelay(10);
 128        }
 129
 130        /*
 131         * now the secondary core is starting up let it run its
 132         * calibrations, then wait for it to finish
 133         */
 134        spin_unlock(&boot_lock);
 135
 136        return pen_release != -1 ? -ENOSYS : 0;
 137}
 138
 139static void __init poke_milo(void)
 140{
 141        /* nobody is to be released from the pen yet */
 142        pen_release = -1;
 143
 144        /*
 145         * Write the address of secondary startup into the system-wide flags
 146         * register. The BootMonitor waits for this register to become
 147         * non-zero.
 148         */
 149        __raw_writel(BSYM(virt_to_phys(realview_secondary_startup)),
 150                     __io_address(REALVIEW_SYS_FLAGSSET));
 151
 152        mb();
 153}
 154
 155/*
 156 * Initialise the CPU possible map early - this describes the CPUs
 157 * which may be present or become present in the system.
 158 */
 159void __init smp_init_cpus(void)
 160{
 161        unsigned int i, ncores = get_core_count();
 162
 163        for (i = 0; i < ncores; i++)
 164                set_cpu_possible(i, true);
 165}
 166
 167void __init smp_prepare_cpus(unsigned int max_cpus)
 168{
 169        unsigned int ncores = get_core_count();
 170        unsigned int cpu = smp_processor_id();
 171        int i;
 172
 173        /* sanity check */
 174        if (ncores == 0) {
 175                printk(KERN_ERR
 176                       "Realview: strange CM count of 0? Default to 1\n");
 177
 178                ncores = 1;
 179        }
 180
 181        if (ncores > NR_CPUS) {
 182                printk(KERN_WARNING
 183                       "Realview: no. of cores (%d) greater than configured "
 184                       "maximum of %d - clipping\n",
 185                       ncores, NR_CPUS);
 186                ncores = NR_CPUS;
 187        }
 188
 189        smp_store_cpu_info(cpu);
 190
 191        /*
 192         * are we trying to boot more cores than exist?
 193         */
 194        if (max_cpus > ncores)
 195                max_cpus = ncores;
 196
 197        /*
 198         * Initialise the present map, which describes the set of CPUs
 199         * actually populated at the present time.
 200         */
 201        for (i = 0; i < max_cpus; i++)
 202                set_cpu_present(i, true);
 203
 204        /*
 205         * Initialise the SCU if there are more than one CPU and let
 206         * them know where to start. Note that, on modern versions of
 207         * MILO, the "poke" doesn't actually do anything until each
 208         * individual core is sent a soft interrupt to get it out of
 209         * WFI
 210         */
 211        if (max_cpus > 1) {
 212                /*
 213                 * Enable the local timer or broadcast device for the
 214                 * boot CPU, but only if we have more than one CPU.
 215                 */
 216                percpu_timer_setup();
 217
 218                scu_enable(scu_base_addr());
 219                poke_milo();
 220        }
 221}
 222