1 /* 2 * Copyright (c) 2013 MundoReader S.L. 3 * Author: Heiko Stuebner <heiko@sntech.de> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #include <linux/delay.h> 17 #include <linux/init.h> 18 #include <linux/smp.h> 19 #include <linux/io.h> 20 #include <linux/of.h> 21 #include <linux/of_address.h> 22 23 #include <asm/cacheflush.h> 24 #include <asm/cp15.h> 25 #include <asm/smp_scu.h> 26 #include <asm/smp_plat.h> 27 #include <asm/mach/map.h> 28 29 #include "core.h" 30 31 static void __iomem *scu_base_addr; 32 static void __iomem *sram_base_addr; 33 static int ncores; 34 35 #define PMU_PWRDN_CON 0x08 36 #define PMU_PWRDN_ST 0x0c 37 38 #define PMU_PWRDN_SCU 4 39 40 static void __iomem *pmu_base_addr; 41 42 static inline bool pmu_power_domain_is_on(int pd) 43 { 44 return !(readl_relaxed(pmu_base_addr + PMU_PWRDN_ST) & BIT(pd)); 45 } 46 47 static void pmu_set_power_domain(int pd, bool on) 48 { 49 u32 val = readl_relaxed(pmu_base_addr + PMU_PWRDN_CON); 50 if (on) 51 val &= ~BIT(pd); 52 else 53 val |= BIT(pd); 54 writel(val, pmu_base_addr + PMU_PWRDN_CON); 55 56 while (pmu_power_domain_is_on(pd) != on) { } 57 } 58 59 /* 60 * Handling of CPU cores 61 */ 62 63 static int __cpuinit rockchip_boot_secondary(unsigned int cpu, 64 struct task_struct *idle) 65 { 66 if (!sram_base_addr || !pmu_base_addr) { 67 pr_err("%s: sram or pmu missing for cpu boot\n", __func__); 68 return -ENXIO; 69 } 70 71 if (cpu >= ncores) { 72 pr_err("%s: cpu %d outside maximum number of cpus %d\n", 73 __func__, cpu, ncores); 74 return -ENXIO; 75 } 76 77 /* start the core */ 78 pmu_set_power_domain(0 + cpu, true); 79 80 return 0; 81 } 82 83 /** 84 * rockchip_smp_prepare_sram - populate necessary sram block 85 * Starting cores execute the code residing at the start of the on-chip sram 86 * after power-on. Therefore make sure, this sram region is reserved and 87 * big enough. After this check, copy the trampoline code that directs the 88 * core to the real startup code in ram into the sram-region. 89 * @node: mmio-sram device node 90 */ 91 static int __init rockchip_smp_prepare_sram(struct device_node *node) 92 { 93 unsigned int trampoline_sz = &rockchip_secondary_trampoline_end - 94 &rockchip_secondary_trampoline; 95 struct resource res; 96 unsigned int rsize; 97 int ret; 98 99 ret = of_address_to_resource(node, 0, &res); 100 if (ret < 0) { 101 pr_err("%s: could not get address for node %s\n", 102 __func__, node->full_name); 103 return ret; 104 } 105 106 rsize = resource_size(&res); 107 if (rsize < trampoline_sz) { 108 pr_err("%s: reserved block with size 0x%x is to small for trampoline size 0x%x\n", 109 __func__, rsize, trampoline_sz); 110 return -EINVAL; 111 } 112 113 sram_base_addr = of_iomap(node, 0); 114 115 /* set the boot function for the sram code */ 116 rockchip_boot_fn = virt_to_phys(rockchip_secondary_startup); 117 118 /* copy the trampoline to sram, that runs during startup of the core */ 119 memcpy(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz); 120 flush_cache_all(); 121 outer_clean_range(0, trampoline_sz); 122 123 dsb_sev(); 124 125 return 0; 126 } 127 128 static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus) 129 { 130 struct device_node *node; 131 unsigned int i; 132 133 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 134 if (!node) { 135 pr_err("%s: missing scu\n", __func__); 136 return; 137 } 138 139 scu_base_addr = of_iomap(node, 0); 140 if (!scu_base_addr) { 141 pr_err("%s: could not map scu registers\n", __func__); 142 return; 143 } 144 145 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-smp-sram"); 146 if (!node) { 147 pr_err("%s: could not find sram dt node\n", __func__); 148 return; 149 } 150 151 if (rockchip_smp_prepare_sram(node)) 152 return; 153 154 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu"); 155 if (!node) { 156 pr_err("%s: could not find pmu dt node\n", __func__); 157 return; 158 } 159 160 pmu_base_addr = of_iomap(node, 0); 161 if (!pmu_base_addr) { 162 pr_err("%s: could not map pmu registers\n", __func__); 163 return; 164 } 165 166 /* enable the SCU power domain */ 167 pmu_set_power_domain(PMU_PWRDN_SCU, true); 168 169 /* 170 * While the number of cpus is gathered from dt, also get the number 171 * of cores from the scu to verify this value when booting the cores. 172 */ 173 ncores = scu_get_core_count(scu_base_addr); 174 175 scu_enable(scu_base_addr); 176 177 /* Make sure that all cores except the first are really off */ 178 for (i = 1; i < ncores; i++) 179 pmu_set_power_domain(0 + i, false); 180 } 181 182 #ifdef CONFIG_HOTPLUG_CPU 183 static int rockchip_cpu_kill(unsigned int cpu) 184 { 185 pmu_set_power_domain(0 + cpu, false); 186 return 1; 187 } 188 189 static void rockchip_cpu_die(unsigned int cpu) 190 { 191 v7_exit_coherency_flush(louis); 192 while(1) 193 cpu_do_idle(); 194 } 195 #endif 196 197 static struct smp_operations rockchip_smp_ops __initdata = { 198 .smp_prepare_cpus = rockchip_smp_prepare_cpus, 199 .smp_boot_secondary = rockchip_boot_secondary, 200 #ifdef CONFIG_HOTPLUG_CPU 201 .cpu_kill = rockchip_cpu_kill, 202 .cpu_die = rockchip_cpu_die, 203 #endif 204 }; 205 CPU_METHOD_OF_DECLARE(rk3066_smp, "rockchip,rk3066-smp", &rockchip_smp_ops); 206