linux/arch/arm/mach-ux500/platsmp.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2002 ARM Ltd.
   3 * Copyright (C) 2008 STMicroelctronics.
   4 * Copyright (C) 2009 ST-Ericsson.
   5 * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
   6 *
   7 * This file is based on arm realview platform
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13#include <linux/init.h>
  14#include <linux/errno.h>
  15#include <linux/delay.h>
  16#include <linux/device.h>
  17#include <linux/smp.h>
  18#include <linux/io.h>
  19
  20#include <asm/cacheflush.h>
  21#include <asm/hardware/gic.h>
  22#include <asm/smp_plat.h>
  23#include <asm/smp_scu.h>
  24#include <mach/hardware.h>
  25#include <mach/setup.h>
  26
  27/* This is called from headsmp.S to wakeup the secondary core */
  28extern void u8500_secondary_startup(void);
  29
  30/*
  31 * control for which core is the next to come out of the secondary
  32 * boot "holding pen"
  33 */
  34volatile int pen_release = -1;
  35
  36/*
  37 * Write pen_release in a way that is guaranteed to be visible to all
  38 * observers, irrespective of whether they're taking part in coherency
  39 * or not.  This is necessary for the hotplug code to work reliably.
  40 */
  41static void write_pen_release(int val)
  42{
  43        pen_release = val;
  44        smp_wmb();
  45        __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
  46        outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
  47}
  48
  49static void __iomem *scu_base_addr(void)
  50{
  51        if (cpu_is_u5500())
  52                return __io_address(U5500_SCU_BASE);
  53        else if (cpu_is_u8500())
  54                return __io_address(U8500_SCU_BASE);
  55        else
  56                ux500_unknown_soc();
  57
  58        return NULL;
  59}
  60
  61static DEFINE_SPINLOCK(boot_lock);
  62
  63void __cpuinit platform_secondary_init(unsigned int cpu)
  64{
  65        /*
  66         * if any interrupts are already enabled for the primary
  67         * core (e.g. timer irq), then they will not have been enabled
  68         * for us: do so
  69         */
  70        gic_secondary_init(0);
  71
  72        /*
  73         * let the primary processor know we're out of the
  74         * pen, then head off into the C entry point
  75         */
  76        write_pen_release(-1);
  77
  78        /*
  79         * Synchronise with the boot thread.
  80         */
  81        spin_lock(&boot_lock);
  82        spin_unlock(&boot_lock);
  83}
  84
  85int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
  86{
  87        unsigned long timeout;
  88
  89        /*
  90         * set synchronisation state between this boot processor
  91         * and the secondary one
  92         */
  93        spin_lock(&boot_lock);
  94
  95        /*
  96         * The secondary processor is waiting to be released from
  97         * the holding pen - release it, then wait for it to flag
  98         * that it has been released by resetting pen_release.
  99         */
 100        write_pen_release(cpu_logical_map(cpu));
 101
 102        gic_raise_softirq(cpumask_of(cpu), 1);
 103
 104        timeout = jiffies + (1 * HZ);
 105        while (time_before(jiffies, timeout)) {
 106                if (pen_release == -1)
 107                        break;
 108        }
 109
 110        /*
 111         * now the secondary core is starting up let it run its
 112         * calibrations, then wait for it to finish
 113         */
 114        spin_unlock(&boot_lock);
 115
 116        return pen_release != -1 ? -ENOSYS : 0;
 117}
 118
 119static void __init wakeup_secondary(void)
 120{
 121        void __iomem *backupram;
 122
 123        if (cpu_is_u5500())
 124                backupram = __io_address(U5500_BACKUPRAM0_BASE);
 125        else if (cpu_is_u8500())
 126                backupram = __io_address(U8500_BACKUPRAM0_BASE);
 127        else
 128                ux500_unknown_soc();
 129
 130        /*
 131         * write the address of secondary startup into the backup ram register
 132         * at offset 0x1FF4, then write the magic number 0xA1FEED01 to the
 133         * backup ram register at offset 0x1FF0, which is what boot rom code
 134         * is waiting for. This would wake up the secondary core from WFE
 135         */
 136#define UX500_CPU1_JUMPADDR_OFFSET 0x1FF4
 137        __raw_writel(virt_to_phys(u8500_secondary_startup),
 138                     backupram + UX500_CPU1_JUMPADDR_OFFSET);
 139
 140#define UX500_CPU1_WAKEMAGIC_OFFSET 0x1FF0
 141        __raw_writel(0xA1FEED01,
 142                     backupram + UX500_CPU1_WAKEMAGIC_OFFSET);
 143
 144        /* make sure write buffer is drained */
 145        mb();
 146}
 147
 148/*
 149 * Initialise the CPU possible map early - this describes the CPUs
 150 * which may be present or become present in the system.
 151 */
 152void __init smp_init_cpus(void)
 153{
 154        void __iomem *scu_base = scu_base_addr();
 155        unsigned int i, ncores;
 156
 157        ncores = scu_base ? scu_get_core_count(scu_base) : 1;
 158
 159        /* sanity check */
 160        if (ncores > nr_cpu_ids) {
 161                pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
 162                        ncores, nr_cpu_ids);
 163                ncores = nr_cpu_ids;
 164        }
 165
 166        for (i = 0; i < ncores; i++)
 167                set_cpu_possible(i, true);
 168
 169        set_smp_cross_call(gic_raise_softirq);
 170}
 171
 172void __init platform_smp_prepare_cpus(unsigned int max_cpus)
 173{
 174
 175        scu_enable(scu_base_addr());
 176        wakeup_secondary();
 177}
 178