Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | /*
* linux/arch/arm/mach-vexpress/platsmp.c
*
* Copyright (C) 2002 ARM Ltd.
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of_fdt.h>
#include <linux/vexpress.h>
#include <asm/smp_scu.h>
#include <asm/mach/map.h>
#include <mach/motherboard.h>
#include <plat/platsmp.h>
#include "core.h"
#if defined(CONFIG_OF)
static enum {
GENERIC_SCU,
CORTEX_A9_SCU,
} vexpress_dt_scu __initdata = GENERIC_SCU;
static struct map_desc vexpress_dt_cortex_a9_scu_map __initdata = {
.virtual = V2T_PERIPH,
/* .pfn set in vexpress_dt_init_cortex_a9_scu() */
.length = SZ_128,
.type = MT_DEVICE,
};
static void *vexpress_dt_cortex_a9_scu_base __initdata;
const static char *vexpress_dt_cortex_a9_match[] __initconst = {
"arm,cortex-a5-scu",
"arm,cortex-a9-scu",
NULL
};
static int __init vexpress_dt_find_scu(unsigned long node,
const char *uname, int depth, void *data)
{
if (of_flat_dt_match(node, vexpress_dt_cortex_a9_match)) {
phys_addr_t phys_addr;
__be32 *reg = of_get_flat_dt_prop(node, "reg", NULL);
if (WARN_ON(!reg))
return -EINVAL;
phys_addr = be32_to_cpup(reg);
vexpress_dt_scu = CORTEX_A9_SCU;
vexpress_dt_cortex_a9_scu_map.pfn = __phys_to_pfn(phys_addr);
iotable_init(&vexpress_dt_cortex_a9_scu_map, 1);
vexpress_dt_cortex_a9_scu_base = ioremap(phys_addr, SZ_256);
if (WARN_ON(!vexpress_dt_cortex_a9_scu_base))
return -EFAULT;
}
return 0;
}
void __init vexpress_dt_smp_map_io(void)
{
if (initial_boot_params)
WARN_ON(of_scan_flat_dt(vexpress_dt_find_scu, NULL));
}
static int __init vexpress_dt_cpus_num(unsigned long node, const char *uname,
int depth, void *data)
{
static int prev_depth = -1;
static int nr_cpus = -1;
if (prev_depth > depth && nr_cpus > 0)
return nr_cpus;
if (nr_cpus < 0 && strcmp(uname, "cpus") == 0)
nr_cpus = 0;
if (nr_cpus >= 0) {
const char *device_type = of_get_flat_dt_prop(node,
"device_type", NULL);
if (device_type && strcmp(device_type, "cpu") == 0)
nr_cpus++;
}
prev_depth = depth;
return 0;
}
static void __init vexpress_dt_smp_init_cpus(void)
{
int ncores = 0, i;
switch (vexpress_dt_scu) {
case GENERIC_SCU:
ncores = of_scan_flat_dt(vexpress_dt_cpus_num, NULL);
break;
case CORTEX_A9_SCU:
ncores = scu_get_core_count(vexpress_dt_cortex_a9_scu_base);
break;
default:
WARN_ON(1);
break;
}
if (ncores < 2)
return;
if (ncores > nr_cpu_ids) {
pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
ncores, nr_cpu_ids);
ncores = nr_cpu_ids;
}
for (i = 0; i < ncores; ++i)
set_cpu_possible(i, true);
}
static void __init vexpress_dt_smp_prepare_cpus(unsigned int max_cpus)
{
int i;
switch (vexpress_dt_scu) {
case GENERIC_SCU:
for (i = 0; i < max_cpus; i++)
set_cpu_present(i, true);
break;
case CORTEX_A9_SCU:
scu_enable(vexpress_dt_cortex_a9_scu_base);
break;
default:
WARN_ON(1);
break;
}
}
#else
static void __init vexpress_dt_smp_init_cpus(void)
{
WARN_ON(1);
}
void __init vexpress_dt_smp_prepare_cpus(unsigned int max_cpus)
{
WARN_ON(1);
}
#endif
/*
* Initialise the CPU possible map early - this describes the CPUs
* which may be present or become present in the system.
*/
static void __init vexpress_smp_init_cpus(void)
{
if (ct_desc)
ct_desc->init_cpu_map();
else
vexpress_dt_smp_init_cpus();
}
static void __init vexpress_smp_prepare_cpus(unsigned int max_cpus)
{
/*
* Initialise the present map, which describes the set of CPUs
* actually populated at the present time.
*/
if (ct_desc)
ct_desc->smp_enable(max_cpus);
else
vexpress_dt_smp_prepare_cpus(max_cpus);
/*
* Write the address of secondary startup into the
* system-wide flags register. The boot monitor waits
* until it receives a soft interrupt, and then the
* secondary CPU branches to this address.
*/
vexpress_flags_set(virt_to_phys(versatile_secondary_startup));
}
struct smp_operations __initdata vexpress_smp_ops = {
.smp_init_cpus = vexpress_smp_init_cpus,
.smp_prepare_cpus = vexpress_smp_prepare_cpus,
.smp_secondary_init = versatile_secondary_init,
.smp_boot_secondary = versatile_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = vexpress_cpu_die,
#endif
};
|