|  | /* | 
|  | * linux/arch/arm/mach-axxia/platsmp.c | 
|  | * | 
|  | * Copyright (C) 2012 LSI Corporation | 
|  | * | 
|  | * 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/io.h> | 
|  | #include <linux/smp.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_address.h> | 
|  | #include <asm/cacheflush.h> | 
|  |  | 
|  | /* Syscon register offsets for releasing cores from reset */ | 
|  | #define SC_CRIT_WRITE_KEY	0x1000 | 
|  | #define SC_RST_CPU_HOLD		0x1010 | 
|  |  | 
|  | /* | 
|  | * Write the kernel entry point for secondary CPUs to the specified address | 
|  | */ | 
|  | static void write_release_addr(u32 release_phys) | 
|  | { | 
|  | u32 *virt = (u32 *) phys_to_virt(release_phys); | 
|  | writel_relaxed(__pa_symbol(secondary_startup), virt); | 
|  | /* Make sure this store is visible to other CPUs */ | 
|  | smp_wmb(); | 
|  | __cpuc_flush_dcache_area(virt, sizeof(u32)); | 
|  | } | 
|  |  | 
|  | static int axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) | 
|  | { | 
|  | struct device_node *syscon_np; | 
|  | void __iomem *syscon; | 
|  | u32 tmp; | 
|  |  | 
|  | syscon_np = of_find_compatible_node(NULL, NULL, "lsi,axxia-syscon"); | 
|  | if (!syscon_np) | 
|  | return -ENOENT; | 
|  |  | 
|  | syscon = of_iomap(syscon_np, 0); | 
|  | if (!syscon) | 
|  | return -ENOMEM; | 
|  |  | 
|  | tmp = readl(syscon + SC_RST_CPU_HOLD); | 
|  | writel(0xab, syscon + SC_CRIT_WRITE_KEY); | 
|  | tmp &= ~(1 << cpu); | 
|  | writel(tmp, syscon + SC_RST_CPU_HOLD); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void __init axxia_smp_prepare_cpus(unsigned int max_cpus) | 
|  | { | 
|  | int cpu_count = 0; | 
|  | int cpu; | 
|  |  | 
|  | /* | 
|  | * Initialise the present map, which describes the set of CPUs actually | 
|  | * populated at the present time. | 
|  | */ | 
|  | for_each_possible_cpu(cpu) { | 
|  | struct device_node *np; | 
|  | u32 release_phys; | 
|  |  | 
|  | np = of_get_cpu_node(cpu, NULL); | 
|  | if (!np) | 
|  | continue; | 
|  | if (of_property_read_u32(np, "cpu-release-addr", &release_phys)) | 
|  | continue; | 
|  |  | 
|  | if (cpu_count < max_cpus) { | 
|  | set_cpu_present(cpu, true); | 
|  | cpu_count++; | 
|  | } | 
|  |  | 
|  | if (release_phys != 0) | 
|  | write_release_addr(release_phys); | 
|  | } | 
|  | } | 
|  |  | 
|  | static const struct smp_operations axxia_smp_ops __initconst = { | 
|  | .smp_prepare_cpus	= axxia_smp_prepare_cpus, | 
|  | .smp_boot_secondary	= axxia_boot_secondary, | 
|  | }; | 
|  | CPU_METHOD_OF_DECLARE(axxia_smp, "lsi,syscon-release", &axxia_smp_ops); |