5f0a2db30a59fa816bd410a0145752622cd13f99
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rk30 / platsmp.c
1 /*
2  * RK30 SMP source file. It contains platform specific fucntions
3  * needed for the linux smp kernel.
4  *
5  * Copyright (C) 2012 ROCKCHIP, Inc.
6  * All Rights Reserved
7  */
8
9 #include <linux/init.h>
10 #include <linux/smp.h>
11 #include <linux/io.h>
12 #include <linux/version.h>
13
14 #include <asm/cacheflush.h>
15 #include <asm/fiq_glue.h>
16 #include <asm/hardware/gic.h>
17 #include <asm/smp_scu.h>
18
19 #include <mach/pmu.h>
20
21 #define SCU_CTRL             0x00
22 #define   SCU_STANDBY_EN     (1 << 5)
23
24 #ifdef CONFIG_FIQ
25 static void gic_raise_softirq_non_secure(const struct cpumask *mask, unsigned int irq)
26 {
27 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
28         unsigned long map = *cpus_addr(*mask);
29 #else
30         int cpu;
31         unsigned long map = 0;
32
33         /* Convert our logical CPU mask into a physical one. */
34         for_each_cpu(cpu, mask)
35                 map |= 1 << cpu_logical_map(cpu);
36 #endif
37
38         /*
39          * Ensure that stores to Normal memory are visible to the
40          * other CPUs before issuing the IPI.
41          */
42         dsb();
43
44         /* this always happens on GIC0 */
45         writel_relaxed(map << 16 | irq | 0x8000, RK30_GICD_BASE + GIC_DIST_SOFTINT);
46 }
47
48 static void gic_secondary_init_non_secure(void)
49 {
50 #define GIC_DIST_SECURITY       0x080
51         writel_relaxed(0xffffffff, RK30_GICD_BASE + GIC_DIST_SECURITY);
52         writel_relaxed(0xf, RK30_GICC_BASE + GIC_CPU_CTRL);
53         dsb();
54 }
55 #endif
56
57 void __cpuinit platform_secondary_init(unsigned int cpu)
58 {
59         /*
60          * if any interrupts are already enabled for the primary
61          * core (e.g. timer irq), then they will not have been enabled
62          * for us: do so
63          */
64         gic_secondary_init(0);
65
66 #ifdef CONFIG_FIQ
67         gic_secondary_init_non_secure();
68         fiq_glue_resume();
69 #endif
70 }
71
72 extern void rk30_sram_secondary_startup(void);
73
74 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
75 {
76         static bool first = true;
77
78         if (first) {
79                 unsigned long sz = 0x10;
80                 unsigned int i, ncores = scu_get_core_count(RK30_SCU_BASE);
81
82                 for (i = 1; i < ncores; i++)
83                         pmu_set_power_domain(PD_A9_0 + i, false);
84
85                 memcpy(RK30_IMEM_BASE, rk30_sram_secondary_startup, sz);
86                 flush_icache_range((unsigned long)RK30_IMEM_BASE, (unsigned long)RK30_IMEM_BASE + sz);
87                 outer_clean_range(0, sz);
88
89                 first = false;
90         }
91
92         dsb_sev();
93         pmu_set_power_domain(PD_A9_0 + cpu, true);
94
95         return 0;
96 }
97
98 /*
99  * Initialise the CPU possible map early - this describes the CPUs
100  * which may be present or become present in the system.
101  */
102 void __init smp_init_cpus(void)
103 {
104         unsigned int i, ncores = scu_get_core_count(RK30_SCU_BASE);
105
106         if (ncores > nr_cpu_ids) {
107                 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
108                         ncores, nr_cpu_ids);
109                 ncores = nr_cpu_ids;
110         }
111
112         for (i = 0; i < ncores; i++)
113                 set_cpu_possible(i, true);
114
115 #ifdef CONFIG_FIQ
116         set_smp_cross_call(gic_raise_softirq_non_secure);
117 #else
118         set_smp_cross_call(gic_raise_softirq);
119 #endif
120 }
121
122 void __init platform_smp_prepare_cpus(unsigned int max_cpus)
123 {
124 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0))
125         int i;
126
127         /*
128          * Initialise the present map, which describes the set of CPUs
129          * actually populated at the present time.
130          */
131         for (i = 0; i < max_cpus; i++)
132                 set_cpu_present(i, true);
133 #endif
134
135         writel_relaxed(readl_relaxed(RK30_SCU_BASE + SCU_CTRL) | SCU_STANDBY_EN, RK30_SCU_BASE + SCU_CTRL);
136
137         scu_enable(RK30_SCU_BASE);
138 }