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/smp_scu.h> 25 #include <asm/smp_plat.h> 26 #include <asm/mach/map.h> 27 28 #include "core.h" 29 30 static void __iomem *scu_base_addr; 31 static void __iomem *sram_base_addr; 32 static int ncores; 33 34 #define PMU_PWRDN_CON 0x08 35 #define PMU_PWRDN_ST 0x0c 36 37 #define PMU_PWRDN_SCU 4 38 39 static void __iomem *pmu_base_addr; 40 41 static inline bool pmu_power_domain_is_on(int pd) 42 { 43 return !(readl_relaxed(pmu_base_addr + PMU_PWRDN_ST) & BIT(pd)); 44 } 45 46 static void pmu_set_power_domain(int pd, bool on) 47 { 48 u32 val = readl_relaxed(pmu_base_addr + PMU_PWRDN_CON); 49 if (on) 50 val &= ~BIT(pd); 51 else 52 val |= BIT(pd); 53 writel(val, pmu_base_addr + PMU_PWRDN_CON); 54 55 while (pmu_power_domain_is_on(pd) != on) { } 56 } 57 58 /* 59 * Handling of CPU cores 60 */ 61 62 static int __cpuinit rockchip_boot_secondary(unsigned int cpu, 63 struct task_struct *idle) 64 { 65 if (!sram_base_addr || !pmu_base_addr) { 66 pr_err("%s: sram or pmu missing for cpu boot\n", __func__); 67 return -ENXIO; 68 } 69 70 if (cpu >= ncores) { 71 pr_err("%s: cpu %d outside maximum number of cpus %d\n", 72 __func__, cpu, ncores); 73 return -ENXIO; 74 } 75 76 /* start the core */ 77 pmu_set_power_domain(0 + cpu, true); 78 79 return 0; 80 } 81 82 /** 83 * rockchip_smp_prepare_sram - populate necessary sram block 84 * Starting cores execute the code residing at the start of the on-chip sram 85 * after power-on. Therefore make sure, this sram region is reserved and 86 * big enough. After this check, copy the trampoline code that directs the 87 * core to the real startup code in ram into the sram-region. 88 * @node: mmio-sram device node 89 */ 90 static int __init rockchip_smp_prepare_sram(struct device_node *node) 91 { 92 unsigned int trampoline_sz = &rockchip_secondary_trampoline_end - 93 &rockchip_secondary_trampoline; 94 struct resource res; 95 unsigned int rsize; 96 int ret; 97 98 ret = of_address_to_resource(node, 0, &res); 99 if (ret < 0) { 100 pr_err("%s: could not get address for node %s\n", 101 __func__, node->full_name); 102 return ret; 103 } 104 105 rsize = resource_size(&res); 106 if (rsize < trampoline_sz) { 107 pr_err("%s: reserved block with size 0x%x is to small for trampoline size 0x%x\n", 108 __func__, rsize, trampoline_sz); 109 return -EINVAL; 110 } 111 112 sram_base_addr = of_iomap(node, 0); 113 114 /* set the boot function for the sram code */ 115 rockchip_boot_fn = virt_to_phys(rockchip_secondary_startup); 116 117 /* copy the trampoline to sram, that runs during startup of the core */ 118 memcpy(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz); 119 flush_cache_all(); 120 outer_clean_range(0, trampoline_sz); 121 122 dsb_sev(); 123 124 return 0; 125 } 126 127 static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus) 128 { 129 struct device_node *node; 130 unsigned int i; 131 132 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 133 if (!node) { 134 pr_err("%s: missing scu\n", __func__); 135 return; 136 } 137 138 scu_base_addr = of_iomap(node, 0); 139 if (!scu_base_addr) { 140 pr_err("%s: could not map scu registers\n", __func__); 141 return; 142 } 143 144 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-smp-sram"); 145 if (!node) { 146 pr_err("%s: could not find sram dt node\n", __func__); 147 return; 148 } 149 150 if (rockchip_smp_prepare_sram(node)) 151 return; 152 153 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu"); 154 if (!node) { 155 pr_err("%s: could not find pmu dt node\n", __func__); 156 return; 157 } 158 159 pmu_base_addr = of_iomap(node, 0); 160 if (!pmu_base_addr) { 161 pr_err("%s: could not map pmu registers\n", __func__); 162 return; 163 } 164 165 /* enable the SCU power domain */ 166 pmu_set_power_domain(PMU_PWRDN_SCU, true); 167 168 /* 169 * While the number of cpus is gathered from dt, also get the number 170 * of cores from the scu to verify this value when booting the cores. 171 */ 172 ncores = scu_get_core_count(scu_base_addr); 173 174 scu_enable(scu_base_addr); 175 176 /* Make sure that all cores except the first are really off */ 177 for (i = 1; i < ncores; i++) 178 pmu_set_power_domain(0 + i, false); 179 } 180 181 static struct smp_operations rockchip_smp_ops __initdata = { 182 .smp_prepare_cpus = rockchip_smp_prepare_cpus, 183 .smp_boot_secondary = rockchip_boot_secondary, 184 }; 185 CPU_METHOD_OF_DECLARE(rk3066_smp, "rockchip,rk3066-smp", &rockchip_smp_ops); 186