0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/export.h>
0014 #include <linux/jiffies.h>
0015 #include <linux/init.h>
0016 #include <linux/io.h>
0017 #include <asm/cacheflush.h>
0018 #include <asm/smp_plat.h>
0019 #include <asm/smp_scu.h>
0020 #include <linux/irqchip/arm-gic.h>
0021 #include "common.h"
0022
0023
0024
0025
0026
0027
0028 static int ncores;
0029
0030 int zynq_cpun_start(u32 address, int cpu)
0031 {
0032 u32 trampoline_code_size = &zynq_secondary_trampoline_end -
0033 &zynq_secondary_trampoline;
0034 u32 phy_cpuid = cpu_logical_map(cpu);
0035
0036
0037
0038 if (!(address & 3) && (!address || (address >= trampoline_code_size))) {
0039
0040 static u8 __iomem *zero;
0041 u32 trampoline_size = &zynq_secondary_trampoline_jump -
0042 &zynq_secondary_trampoline;
0043
0044 zynq_slcr_cpu_stop(phy_cpuid);
0045 if (address) {
0046 if (__pa(PAGE_OFFSET)) {
0047 zero = ioremap(0, trampoline_code_size);
0048 if (!zero) {
0049 pr_warn("BOOTUP jump vectors not accessible\n");
0050 return -1;
0051 }
0052 } else {
0053 zero = (__force u8 __iomem *)PAGE_OFFSET;
0054 }
0055
0056
0057
0058
0059
0060
0061
0062 memcpy_toio(zero, &zynq_secondary_trampoline,
0063 trampoline_size);
0064 writel(address, zero + trampoline_size);
0065
0066 flush_cache_all();
0067 outer_flush_range(0, trampoline_code_size);
0068 smp_wmb();
0069
0070 if (__pa(PAGE_OFFSET))
0071 iounmap(zero);
0072 }
0073 zynq_slcr_cpu_start(phy_cpuid);
0074
0075 return 0;
0076 }
0077
0078 pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);
0079
0080 return -1;
0081 }
0082 EXPORT_SYMBOL(zynq_cpun_start);
0083
0084 static int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle)
0085 {
0086 return zynq_cpun_start(__pa_symbol(secondary_startup_arm), cpu);
0087 }
0088
0089
0090
0091
0092
0093 static void __init zynq_smp_init_cpus(void)
0094 {
0095 int i;
0096
0097 ncores = scu_get_core_count(zynq_scu_base);
0098
0099 for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++)
0100 set_cpu_possible(i, true);
0101 }
0102
0103 static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
0104 {
0105 scu_enable(zynq_scu_base);
0106 }
0107
0108
0109
0110
0111
0112
0113
0114
0115 static void zynq_secondary_init(unsigned int cpu)
0116 {
0117 zynq_core_pm_init();
0118 }
0119
0120 #ifdef CONFIG_HOTPLUG_CPU
0121 static int zynq_cpu_kill(unsigned cpu)
0122 {
0123 unsigned long timeout = jiffies + msecs_to_jiffies(50);
0124
0125 while (zynq_slcr_cpu_state_read(cpu))
0126 if (time_after(jiffies, timeout))
0127 return 0;
0128
0129 zynq_slcr_cpu_stop(cpu);
0130 return 1;
0131 }
0132
0133
0134
0135
0136
0137
0138
0139
0140 static void zynq_cpu_die(unsigned int cpu)
0141 {
0142 zynq_slcr_cpu_state_write(cpu, true);
0143
0144
0145
0146
0147
0148
0149 for (;;)
0150 cpu_do_idle();
0151 }
0152 #endif
0153
0154 const struct smp_operations zynq_smp_ops __initconst = {
0155 .smp_init_cpus = zynq_smp_init_cpus,
0156 .smp_prepare_cpus = zynq_smp_prepare_cpus,
0157 .smp_boot_secondary = zynq_boot_secondary,
0158 .smp_secondary_init = zynq_secondary_init,
0159 #ifdef CONFIG_HOTPLUG_CPU
0160 .cpu_die = zynq_cpu_die,
0161 .cpu_kill = zynq_cpu_kill,
0162 #endif
0163 };