linux/arch/arm/mach-msm/platsmp.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2002 ARM Ltd.
   3 *  All Rights Reserved
   4 *  Copyright (c) 2010, Code Aurora Forum. 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 as
   8 * published by the Free Software Foundation.
   9 */
  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 <asm/cputype.h>
  21#include <asm/mach-types.h>
  22#include <asm/smp_plat.h>
  23
  24#include "scm-boot.h"
  25#include "common.h"
  26
  27#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
  28#define SCSS_CPU1CORE_RESET 0xD80
  29#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
  30
  31extern void msm_secondary_startup(void);
  32
  33static DEFINE_SPINLOCK(boot_lock);
  34
  35static inline int get_core_count(void)
  36{
  37        /* 1 + the PART[1:0] field of MIDR */
  38        return ((read_cpuid_id() >> 4) & 3) + 1;
  39}
  40
  41static void __cpuinit msm_secondary_init(unsigned int cpu)
  42{
  43        /*
  44         * let the primary processor know we're out of the
  45         * pen, then head off into the C entry point
  46         */
  47        pen_release = -1;
  48        smp_wmb();
  49
  50        /*
  51         * Synchronise with the boot thread.
  52         */
  53        spin_lock(&boot_lock);
  54        spin_unlock(&boot_lock);
  55}
  56
  57static __cpuinit void prepare_cold_cpu(unsigned int cpu)
  58{
  59        int ret;
  60        ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup),
  61                                SCM_FLAG_COLDBOOT_CPU1);
  62        if (ret == 0) {
  63                void __iomem *sc1_base_ptr;
  64                sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
  65                if (sc1_base_ptr) {
  66                        writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
  67                        writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
  68                        writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
  69                        iounmap(sc1_base_ptr);
  70                }
  71        } else
  72                printk(KERN_DEBUG "Failed to set secondary core boot "
  73                                  "address\n");
  74}
  75
  76static int __cpuinit msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
  77{
  78        unsigned long timeout;
  79        static int cold_boot_done;
  80
  81        /* Only need to bring cpu out of reset this way once */
  82        if (cold_boot_done == false) {
  83                prepare_cold_cpu(cpu);
  84                cold_boot_done = true;
  85        }
  86
  87        /*
  88         * set synchronisation state between this boot processor
  89         * and the secondary one
  90         */
  91        spin_lock(&boot_lock);
  92
  93        /*
  94         * The secondary processor is waiting to be released from
  95         * the holding pen - release it, then wait for it to flag
  96         * that it has been released by resetting pen_release.
  97         *
  98         * Note that "pen_release" is the hardware CPU ID, whereas
  99         * "cpu" is Linux's internal ID.
 100         */
 101        pen_release = cpu_logical_map(cpu);
 102        __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
 103        outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
 104
 105        /*
 106         * Send the secondary CPU a soft interrupt, thereby causing
 107         * the boot monitor to read the system wide flags register,
 108         * and branch to the address found there.
 109         */
 110        arch_send_wakeup_ipi_mask(cpumask_of(cpu));
 111
 112        timeout = jiffies + (1 * HZ);
 113        while (time_before(jiffies, timeout)) {
 114                smp_rmb();
 115                if (pen_release == -1)
 116                        break;
 117
 118                udelay(10);
 119        }
 120
 121        /*
 122         * now the secondary core is starting up let it run its
 123         * calibrations, then wait for it to finish
 124         */
 125        spin_unlock(&boot_lock);
 126
 127        return pen_release != -1 ? -ENOSYS : 0;
 128}
 129
 130/*
 131 * Initialise the CPU possible map early - this describes the CPUs
 132 * which may be present or become present in the system. The msm8x60
 133 * does not support the ARM SCU, so just set the possible cpu mask to
 134 * NR_CPUS.
 135 */
 136static void __init msm_smp_init_cpus(void)
 137{
 138        unsigned int i, ncores = get_core_count();
 139
 140        if (ncores > nr_cpu_ids) {
 141                pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
 142                        ncores, nr_cpu_ids);
 143                ncores = nr_cpu_ids;
 144        }
 145
 146        for (i = 0; i < ncores; i++)
 147                set_cpu_possible(i, true);
 148}
 149
 150static void __init msm_smp_prepare_cpus(unsigned int max_cpus)
 151{
 152}
 153
 154struct smp_operations msm_smp_ops __initdata = {
 155        .smp_init_cpus          = msm_smp_init_cpus,
 156        .smp_prepare_cpus       = msm_smp_prepare_cpus,
 157        .smp_secondary_init     = msm_secondary_init,
 158        .smp_boot_secondary     = msm_boot_secondary,
 159#ifdef CONFIG_HOTPLUG_CPU
 160        .cpu_die                = msm_cpu_die,
 161#endif
 162};
 163