alpine_machdep_mp.c revision 296098
1/*- 2 * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> 3 * Copyright (c) 2015 Semihalf 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/sys/arm/annapurna/alpine/alpine_machdep_mp.c 296098 2016-02-26 15:54:34Z andrew $"); 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/lock.h> 35#include <sys/mutex.h> 36#include <sys/smp.h> 37#include <sys/cpuset.h> 38 39#include <vm/vm.h> 40#include <vm/pmap.h> 41 42#include <machine/smp.h> 43#include <machine/fdt.h> 44#include <machine/intr.h> 45#include <machine/cpu-v6.h> 46 47#include <dev/fdt/fdt_common.h> 48#include <dev/ofw/ofw_cpu.h> 49#include <dev/ofw/ofw_bus_subr.h> 50 51#define AL_CPU_RESUME_WATERMARK_REG 0x00 52#define AL_CPU_RESUME_FLAGS_REG 0x04 53#define AL_CPU_RESUME_PCPU_RADDR_REG(cpu) (0x08 + 0x04 + 8*(cpu)) 54#define AL_CPU_RESUME_PCPU_FLAGS(cpu) (0x08 + 8*(cpu)) 55 56/* Per-CPU flags */ 57#define AL_CPU_RESUME_FLG_PERCPU_DONT_RESUME (1 << 2) 58 59/* The expected magic number for validating the resume addresses */ 60#define AL_CPU_RESUME_MAGIC_NUM 0xf0e1d200 61#define AL_CPU_RESUME_MAGIC_NUM_MASK 0xffffff00 62 63/* The expected minimal version number for validating the capabilities */ 64#define AL_CPU_RESUME_MIN_VER 0x000000c3 65#define AL_CPU_RESUME_MIN_VER_MASK 0x000000ff 66 67/* Field controlling the boot-up of companion cores */ 68#define AL_NB_INIT_CONTROL (0x8) 69#define AL_NB_CONFIG_STATUS_PWR_CTRL(cpu) (0x2020 + (cpu)*0x100) 70 71#define SERDES_NUM_GROUPS 4 72#define SERDES_GROUP_SIZE 0x400 73 74extern bus_addr_t al_devmap_pa; 75extern bus_addr_t al_devmap_size; 76 77extern void mpentry(void); 78 79int alpine_serdes_resource_get(uint32_t group, bus_space_tag_t *tag, 80 bus_addr_t *baddr); 81static int platform_mp_get_core_cnt(void); 82static int alpine_get_cpu_resume_base(u_long *pbase, u_long *psize); 83static int alpine_get_nb_base(u_long *pbase, u_long *psize); 84static int alpine_get_serdes_base(u_long *pbase, u_long *psize); 85int alpine_serdes_resource_get(uint32_t group, bus_space_tag_t *tag, 86 bus_addr_t *baddr); 87static boolean_t alpine_validate_cpu(u_int, phandle_t, u_int, pcell_t *); 88 89static boolean_t 90alpine_validate_cpu(u_int id, phandle_t child, u_int addr_cell, pcell_t *reg) 91{ 92 return fdt_is_compatible(child, "arm,cortex-a15"); 93} 94 95static int 96platform_mp_get_core_cnt(void) 97{ 98 static int ncores = 0; 99 int nchilds; 100 uint32_t reg; 101 102 /* Calculate ncores value only once */ 103 if (ncores) 104 return (ncores); 105 106 reg = cp15_l2ctlr_get(); 107 ncores = CPUV7_L2CTLR_NPROC(reg); 108 109 nchilds = ofw_cpu_early_foreach(alpine_validate_cpu, false); 110 111 /* Limit CPUs if DTS has configured less than available */ 112 if ((nchilds > 0) && (nchilds < ncores)) { 113 printf("SMP: limiting number of active CPUs to %d out of %d\n", 114 nchilds, ncores); 115 ncores = nchilds; 116 } 117 118 return (ncores); 119} 120 121void 122platform_mp_init_secondary(void) 123{ 124 125 intr_pic_init_secondary(); 126} 127 128void 129platform_mp_setmaxid(void) 130{ 131 132 mp_ncpus = platform_mp_get_core_cnt(); 133 mp_maxid = mp_ncpus - 1; 134} 135 136static int 137alpine_get_cpu_resume_base(u_long *pbase, u_long *psize) 138{ 139 phandle_t node; 140 u_long base = 0; 141 u_long size = 0; 142 143 if (pbase == NULL || psize == NULL) 144 return (EINVAL); 145 146 if ((node = OF_finddevice("/")) == -1) 147 return (EFAULT); 148 149 if ((node = 150 ofw_bus_find_compatible(node, "annapurna-labs,al-cpu-resume")) == 0) 151 return (EFAULT); 152 153 if (fdt_regsize(node, &base, &size)) 154 return (EFAULT); 155 156 *pbase = base; 157 *psize = size; 158 159 return (0); 160} 161 162static int 163alpine_get_nb_base(u_long *pbase, u_long *psize) 164{ 165 phandle_t node; 166 u_long base = 0; 167 u_long size = 0; 168 169 if (pbase == NULL || psize == NULL) 170 return (EINVAL); 171 172 if ((node = OF_finddevice("/")) == -1) 173 return (EFAULT); 174 175 if ((node = 176 ofw_bus_find_compatible(node, "annapurna-labs,al-nb-service")) == 0) 177 return (EFAULT); 178 179 if (fdt_regsize(node, &base, &size)) 180 return (EFAULT); 181 182 *pbase = base; 183 *psize = size; 184 185 return (0); 186} 187 188void 189platform_mp_start_ap(void) 190{ 191 uint32_t physaddr; 192 vm_offset_t vaddr; 193 uint32_t val; 194 uint32_t start_mask; 195 u_long cpu_resume_base; 196 u_long nb_base; 197 u_long cpu_resume_size; 198 u_long nb_size; 199 bus_addr_t cpu_resume_baddr; 200 bus_addr_t nb_baddr; 201 int a; 202 203 if (alpine_get_cpu_resume_base(&cpu_resume_base, &cpu_resume_size)) 204 panic("Couldn't resolve cpu_resume_base address\n"); 205 206 if (alpine_get_nb_base(&nb_base, &nb_size)) 207 panic("Couldn't resolve_nb_base address\n"); 208 209 /* Proceed with start addresses for additional CPUs */ 210 if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + cpu_resume_base, 211 cpu_resume_size, 0, &cpu_resume_baddr)) 212 panic("Couldn't map CPU-resume area"); 213 if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + nb_base, 214 nb_size, 0, &nb_baddr)) 215 panic("Couldn't map NB-service area"); 216 217 /* Proceed with start addresses for additional CPUs */ 218 val = bus_space_read_4(fdtbus_bs_tag, cpu_resume_baddr, 219 AL_CPU_RESUME_WATERMARK_REG); 220 if (((val & AL_CPU_RESUME_MAGIC_NUM_MASK) != AL_CPU_RESUME_MAGIC_NUM) || 221 ((val & AL_CPU_RESUME_MIN_VER_MASK) < AL_CPU_RESUME_MIN_VER)) { 222 panic("CPU-resume device is not compatible"); 223 } 224 225 vaddr = (vm_offset_t)mpentry; 226 physaddr = pmap_kextract(vaddr); 227 228 for (a = 1; a < platform_mp_get_core_cnt(); a++) { 229 /* Power up the core */ 230 bus_space_write_4(fdtbus_bs_tag, nb_baddr, 231 AL_NB_CONFIG_STATUS_PWR_CTRL(a), 0); 232 mb(); 233 234 /* Enable resume */ 235 val = bus_space_read_4(fdtbus_bs_tag, cpu_resume_baddr, 236 AL_CPU_RESUME_PCPU_FLAGS(a)); 237 val &= ~AL_CPU_RESUME_FLG_PERCPU_DONT_RESUME; 238 bus_space_write_4(fdtbus_bs_tag, cpu_resume_baddr, 239 AL_CPU_RESUME_PCPU_FLAGS(a), val); 240 mb(); 241 242 /* Set resume physical address */ 243 bus_space_write_4(fdtbus_bs_tag, cpu_resume_baddr, 244 AL_CPU_RESUME_PCPU_RADDR_REG(a), physaddr); 245 mb(); 246 } 247 248 /* Release cores from reset */ 249 if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + nb_base, 250 nb_size, 0, &nb_baddr)) 251 panic("Couldn't map NB-service area"); 252 253 start_mask = (1 << platform_mp_get_core_cnt()) - 1; 254 255 /* Release cores from reset */ 256 val = bus_space_read_4(fdtbus_bs_tag, nb_baddr, AL_NB_INIT_CONTROL); 257 val |= start_mask; 258 bus_space_write_4(fdtbus_bs_tag, nb_baddr, AL_NB_INIT_CONTROL, val); 259 dsb(); 260 261 bus_space_unmap(fdtbus_bs_tag, nb_baddr, nb_size); 262 bus_space_unmap(fdtbus_bs_tag, cpu_resume_baddr, cpu_resume_size); 263} 264 265static int 266alpine_get_serdes_base(u_long *pbase, u_long *psize) 267{ 268 phandle_t node; 269 u_long base = 0; 270 u_long size = 0; 271 272 if (pbase == NULL || psize == NULL) 273 return (EINVAL); 274 275 if ((node = OF_finddevice("/")) == -1) 276 return (EFAULT); 277 278 if ((node = 279 ofw_bus_find_compatible(node, "annapurna-labs,al-serdes")) == 0) 280 return (EFAULT); 281 282 if (fdt_regsize(node, &base, &size)) 283 return (EFAULT); 284 285 *pbase = base; 286 *psize = size; 287 288 return (0); 289} 290 291int 292alpine_serdes_resource_get(uint32_t group, bus_space_tag_t *tag, bus_addr_t *baddr) 293{ 294 u_long serdes_base, serdes_size; 295 int ret; 296 static bus_addr_t baddr_mapped[SERDES_NUM_GROUPS]; 297 298 if (group >= SERDES_NUM_GROUPS) 299 return (EINVAL); 300 301 if (baddr_mapped[group]) { 302 *tag = fdtbus_bs_tag; 303 *baddr = baddr_mapped[group]; 304 return (0); 305 } 306 307 ret = alpine_get_serdes_base(&serdes_base, &serdes_size); 308 if (ret) 309 return (ret); 310 311 ret = bus_space_map(fdtbus_bs_tag, 312 al_devmap_pa + serdes_base + group * SERDES_GROUP_SIZE, 313 (SERDES_NUM_GROUPS - group) * SERDES_GROUP_SIZE, 0, baddr); 314 if (ret) 315 return (ret); 316 317 baddr_mapped[group] = *baddr; 318 319 return (0); 320} 321