14d892e4fSZbigniew Bodek /*- 24d892e4fSZbigniew Bodek * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> 34d892e4fSZbigniew Bodek * Copyright (c) 2015 Semihalf 44d892e4fSZbigniew Bodek * All rights reserved. 54d892e4fSZbigniew Bodek * 64d892e4fSZbigniew Bodek * Redistribution and use in source and binary forms, with or without 74d892e4fSZbigniew Bodek * modification, are permitted provided that the following conditions 84d892e4fSZbigniew Bodek * are met: 94d892e4fSZbigniew Bodek * 1. Redistributions of source code must retain the above copyright 104d892e4fSZbigniew Bodek * notice, this list of conditions and the following disclaimer. 114d892e4fSZbigniew Bodek * 2. Redistributions in binary form must reproduce the above copyright 124d892e4fSZbigniew Bodek * notice, this list of conditions and the following disclaimer in the 134d892e4fSZbigniew Bodek * documentation and/or other materials provided with the distribution. 144d892e4fSZbigniew Bodek * 154d892e4fSZbigniew Bodek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 164d892e4fSZbigniew Bodek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 174d892e4fSZbigniew Bodek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 184d892e4fSZbigniew Bodek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 194d892e4fSZbigniew Bodek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 204d892e4fSZbigniew Bodek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 214d892e4fSZbigniew Bodek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 224d892e4fSZbigniew Bodek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 234d892e4fSZbigniew Bodek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 244d892e4fSZbigniew Bodek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 254d892e4fSZbigniew Bodek * SUCH DAMAGE. 264d892e4fSZbigniew Bodek * 274d892e4fSZbigniew Bodek */ 284d892e4fSZbigniew Bodek 294d892e4fSZbigniew Bodek #include <sys/cdefs.h> 304d892e4fSZbigniew Bodek __FBSDID("$FreeBSD$"); 314d892e4fSZbigniew Bodek #include <sys/param.h> 324d892e4fSZbigniew Bodek #include <sys/systm.h> 334d892e4fSZbigniew Bodek #include <sys/bus.h> 344d892e4fSZbigniew Bodek #include <sys/lock.h> 354d892e4fSZbigniew Bodek #include <sys/mutex.h> 364d892e4fSZbigniew Bodek #include <sys/smp.h> 374d892e4fSZbigniew Bodek #include <sys/cpuset.h> 384d892e4fSZbigniew Bodek 394d892e4fSZbigniew Bodek #include <vm/vm.h> 404d892e4fSZbigniew Bodek #include <vm/pmap.h> 414d892e4fSZbigniew Bodek 424d892e4fSZbigniew Bodek #include <machine/smp.h> 434d892e4fSZbigniew Bodek #include <machine/fdt.h> 444d892e4fSZbigniew Bodek #include <machine/intr.h> 454d892e4fSZbigniew Bodek #include <machine/cpu-v6.h> 46c20963adSAndrew Turner #include <machine/platformvar.h> 474d892e4fSZbigniew Bodek 484d892e4fSZbigniew Bodek #include <dev/fdt/fdt_common.h> 49222102cfSAndrew Turner #include <dev/ofw/openfirm.h> 504d892e4fSZbigniew Bodek #include <dev/ofw/ofw_cpu.h> 514d892e4fSZbigniew Bodek #include <dev/ofw/ofw_bus_subr.h> 524d892e4fSZbigniew Bodek 53c20963adSAndrew Turner #include <arm/annapurna/alpine/alpine_mp.h> 54c20963adSAndrew Turner 554d892e4fSZbigniew Bodek #define AL_CPU_RESUME_WATERMARK_REG 0x00 564d892e4fSZbigniew Bodek #define AL_CPU_RESUME_FLAGS_REG 0x04 574d892e4fSZbigniew Bodek #define AL_CPU_RESUME_PCPU_RADDR_REG(cpu) (0x08 + 0x04 + 8*(cpu)) 584d892e4fSZbigniew Bodek #define AL_CPU_RESUME_PCPU_FLAGS(cpu) (0x08 + 8*(cpu)) 594d892e4fSZbigniew Bodek 604d892e4fSZbigniew Bodek /* Per-CPU flags */ 614d892e4fSZbigniew Bodek #define AL_CPU_RESUME_FLG_PERCPU_DONT_RESUME (1 << 2) 624d892e4fSZbigniew Bodek 634d892e4fSZbigniew Bodek /* The expected magic number for validating the resume addresses */ 644d892e4fSZbigniew Bodek #define AL_CPU_RESUME_MAGIC_NUM 0xf0e1d200 654d892e4fSZbigniew Bodek #define AL_CPU_RESUME_MAGIC_NUM_MASK 0xffffff00 664d892e4fSZbigniew Bodek 674d892e4fSZbigniew Bodek /* The expected minimal version number for validating the capabilities */ 684d892e4fSZbigniew Bodek #define AL_CPU_RESUME_MIN_VER 0x000000c3 694d892e4fSZbigniew Bodek #define AL_CPU_RESUME_MIN_VER_MASK 0x000000ff 704d892e4fSZbigniew Bodek 714d892e4fSZbigniew Bodek /* Field controlling the boot-up of companion cores */ 724d892e4fSZbigniew Bodek #define AL_NB_INIT_CONTROL (0x8) 734d892e4fSZbigniew Bodek #define AL_NB_CONFIG_STATUS_PWR_CTRL(cpu) (0x2020 + (cpu)*0x100) 744d892e4fSZbigniew Bodek 754d892e4fSZbigniew Bodek extern bus_addr_t al_devmap_pa; 764d892e4fSZbigniew Bodek extern bus_addr_t al_devmap_size; 774d892e4fSZbigniew Bodek 784d892e4fSZbigniew Bodek extern void mpentry(void); 794d892e4fSZbigniew Bodek 804d892e4fSZbigniew Bodek static int platform_mp_get_core_cnt(void); 814d892e4fSZbigniew Bodek static int alpine_get_cpu_resume_base(u_long *pbase, u_long *psize); 824d892e4fSZbigniew Bodek static int alpine_get_nb_base(u_long *pbase, u_long *psize); 834d892e4fSZbigniew Bodek static boolean_t alpine_validate_cpu(u_int, phandle_t, u_int, pcell_t *); 844d892e4fSZbigniew Bodek 854d892e4fSZbigniew Bodek static boolean_t 864d892e4fSZbigniew Bodek alpine_validate_cpu(u_int id, phandle_t child, u_int addr_cell, pcell_t *reg) 874d892e4fSZbigniew Bodek { 8887acb7f8SAndrew Turner return ofw_bus_node_is_compatible(child, "arm,cortex-a15"); 894d892e4fSZbigniew Bodek } 904d892e4fSZbigniew Bodek 914d892e4fSZbigniew Bodek static int 924d892e4fSZbigniew Bodek platform_mp_get_core_cnt(void) 934d892e4fSZbigniew Bodek { 944d892e4fSZbigniew Bodek static int ncores = 0; 954d892e4fSZbigniew Bodek int nchilds; 964d892e4fSZbigniew Bodek uint32_t reg; 974d892e4fSZbigniew Bodek 984d892e4fSZbigniew Bodek /* Calculate ncores value only once */ 994d892e4fSZbigniew Bodek if (ncores) 1004d892e4fSZbigniew Bodek return (ncores); 1014d892e4fSZbigniew Bodek 1024d892e4fSZbigniew Bodek reg = cp15_l2ctlr_get(); 1034d892e4fSZbigniew Bodek ncores = CPUV7_L2CTLR_NPROC(reg); 1044d892e4fSZbigniew Bodek 1054d892e4fSZbigniew Bodek nchilds = ofw_cpu_early_foreach(alpine_validate_cpu, false); 1064d892e4fSZbigniew Bodek 1074d892e4fSZbigniew Bodek /* Limit CPUs if DTS has configured less than available */ 1084d892e4fSZbigniew Bodek if ((nchilds > 0) && (nchilds < ncores)) { 1094d892e4fSZbigniew Bodek printf("SMP: limiting number of active CPUs to %d out of %d\n", 1104d892e4fSZbigniew Bodek nchilds, ncores); 1114d892e4fSZbigniew Bodek ncores = nchilds; 1124d892e4fSZbigniew Bodek } 1134d892e4fSZbigniew Bodek 1144d892e4fSZbigniew Bodek return (ncores); 1154d892e4fSZbigniew Bodek } 1164d892e4fSZbigniew Bodek 1174d892e4fSZbigniew Bodek void 118c20963adSAndrew Turner alpine_mp_setmaxid(platform_t plat) 1194d892e4fSZbigniew Bodek { 1204d892e4fSZbigniew Bodek 12127f38a8dSTijl Coosemans mp_ncpus = platform_mp_get_core_cnt(); 12227f38a8dSTijl Coosemans mp_maxid = mp_ncpus - 1; 1234d892e4fSZbigniew Bodek } 1244d892e4fSZbigniew Bodek 1254d892e4fSZbigniew Bodek static int 1264d892e4fSZbigniew Bodek alpine_get_cpu_resume_base(u_long *pbase, u_long *psize) 1274d892e4fSZbigniew Bodek { 1284d892e4fSZbigniew Bodek phandle_t node; 1294d892e4fSZbigniew Bodek u_long base = 0; 1304d892e4fSZbigniew Bodek u_long size = 0; 1314d892e4fSZbigniew Bodek 1324d892e4fSZbigniew Bodek if (pbase == NULL || psize == NULL) 1334d892e4fSZbigniew Bodek return (EINVAL); 1344d892e4fSZbigniew Bodek 1354d892e4fSZbigniew Bodek if ((node = OF_finddevice("/")) == -1) 1364d892e4fSZbigniew Bodek return (EFAULT); 1374d892e4fSZbigniew Bodek 1384d892e4fSZbigniew Bodek if ((node = 1394d892e4fSZbigniew Bodek ofw_bus_find_compatible(node, "annapurna-labs,al-cpu-resume")) == 0) 1404d892e4fSZbigniew Bodek return (EFAULT); 1414d892e4fSZbigniew Bodek 1424d892e4fSZbigniew Bodek if (fdt_regsize(node, &base, &size)) 1434d892e4fSZbigniew Bodek return (EFAULT); 1444d892e4fSZbigniew Bodek 1454d892e4fSZbigniew Bodek *pbase = base; 1464d892e4fSZbigniew Bodek *psize = size; 1474d892e4fSZbigniew Bodek 1484d892e4fSZbigniew Bodek return (0); 1494d892e4fSZbigniew Bodek } 1504d892e4fSZbigniew Bodek 1514d892e4fSZbigniew Bodek static int 1524d892e4fSZbigniew Bodek alpine_get_nb_base(u_long *pbase, u_long *psize) 1534d892e4fSZbigniew Bodek { 1544d892e4fSZbigniew Bodek phandle_t node; 1554d892e4fSZbigniew Bodek u_long base = 0; 1564d892e4fSZbigniew Bodek u_long size = 0; 1574d892e4fSZbigniew Bodek 1584d892e4fSZbigniew Bodek if (pbase == NULL || psize == NULL) 1594d892e4fSZbigniew Bodek return (EINVAL); 1604d892e4fSZbigniew Bodek 1614d892e4fSZbigniew Bodek if ((node = OF_finddevice("/")) == -1) 1624d892e4fSZbigniew Bodek return (EFAULT); 1634d892e4fSZbigniew Bodek 1644d892e4fSZbigniew Bodek if ((node = 1654d892e4fSZbigniew Bodek ofw_bus_find_compatible(node, "annapurna-labs,al-nb-service")) == 0) 1664d892e4fSZbigniew Bodek return (EFAULT); 1674d892e4fSZbigniew Bodek 1684d892e4fSZbigniew Bodek if (fdt_regsize(node, &base, &size)) 1694d892e4fSZbigniew Bodek return (EFAULT); 1704d892e4fSZbigniew Bodek 1714d892e4fSZbigniew Bodek *pbase = base; 1724d892e4fSZbigniew Bodek *psize = size; 1734d892e4fSZbigniew Bodek 1744d892e4fSZbigniew Bodek return (0); 1754d892e4fSZbigniew Bodek } 1764d892e4fSZbigniew Bodek 1774d892e4fSZbigniew Bodek void 178c20963adSAndrew Turner alpine_mp_start_ap(platform_t plat) 1794d892e4fSZbigniew Bodek { 1804d892e4fSZbigniew Bodek uint32_t physaddr; 1814d892e4fSZbigniew Bodek vm_offset_t vaddr; 1824d892e4fSZbigniew Bodek uint32_t val; 1834d892e4fSZbigniew Bodek uint32_t start_mask; 1844d892e4fSZbigniew Bodek u_long cpu_resume_base; 1854d892e4fSZbigniew Bodek u_long nb_base; 1864d892e4fSZbigniew Bodek u_long cpu_resume_size; 1874d892e4fSZbigniew Bodek u_long nb_size; 1884d892e4fSZbigniew Bodek bus_addr_t cpu_resume_baddr; 1894d892e4fSZbigniew Bodek bus_addr_t nb_baddr; 1904d892e4fSZbigniew Bodek int a; 1914d892e4fSZbigniew Bodek 1924d892e4fSZbigniew Bodek if (alpine_get_cpu_resume_base(&cpu_resume_base, &cpu_resume_size)) 1934d892e4fSZbigniew Bodek panic("Couldn't resolve cpu_resume_base address\n"); 1944d892e4fSZbigniew Bodek 1954d892e4fSZbigniew Bodek if (alpine_get_nb_base(&nb_base, &nb_size)) 1964d892e4fSZbigniew Bodek panic("Couldn't resolve_nb_base address\n"); 1974d892e4fSZbigniew Bodek 1984d892e4fSZbigniew Bodek /* Proceed with start addresses for additional CPUs */ 1994d892e4fSZbigniew Bodek if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + cpu_resume_base, 2004d892e4fSZbigniew Bodek cpu_resume_size, 0, &cpu_resume_baddr)) 2014d892e4fSZbigniew Bodek panic("Couldn't map CPU-resume area"); 2024d892e4fSZbigniew Bodek if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + nb_base, 2034d892e4fSZbigniew Bodek nb_size, 0, &nb_baddr)) 2044d892e4fSZbigniew Bodek panic("Couldn't map NB-service area"); 2054d892e4fSZbigniew Bodek 2064d892e4fSZbigniew Bodek /* Proceed with start addresses for additional CPUs */ 2074d892e4fSZbigniew Bodek val = bus_space_read_4(fdtbus_bs_tag, cpu_resume_baddr, 2084d892e4fSZbigniew Bodek AL_CPU_RESUME_WATERMARK_REG); 2094d892e4fSZbigniew Bodek if (((val & AL_CPU_RESUME_MAGIC_NUM_MASK) != AL_CPU_RESUME_MAGIC_NUM) || 2104d892e4fSZbigniew Bodek ((val & AL_CPU_RESUME_MIN_VER_MASK) < AL_CPU_RESUME_MIN_VER)) { 2114d892e4fSZbigniew Bodek panic("CPU-resume device is not compatible"); 2124d892e4fSZbigniew Bodek } 2134d892e4fSZbigniew Bodek 2144d892e4fSZbigniew Bodek vaddr = (vm_offset_t)mpentry; 2154d892e4fSZbigniew Bodek physaddr = pmap_kextract(vaddr); 2164d892e4fSZbigniew Bodek 2174d892e4fSZbigniew Bodek for (a = 1; a < platform_mp_get_core_cnt(); a++) { 2184d892e4fSZbigniew Bodek /* Power up the core */ 2194d892e4fSZbigniew Bodek bus_space_write_4(fdtbus_bs_tag, nb_baddr, 2204d892e4fSZbigniew Bodek AL_NB_CONFIG_STATUS_PWR_CTRL(a), 0); 2214d892e4fSZbigniew Bodek mb(); 2224d892e4fSZbigniew Bodek 2234d892e4fSZbigniew Bodek /* Enable resume */ 2244d892e4fSZbigniew Bodek val = bus_space_read_4(fdtbus_bs_tag, cpu_resume_baddr, 2254d892e4fSZbigniew Bodek AL_CPU_RESUME_PCPU_FLAGS(a)); 2264d892e4fSZbigniew Bodek val &= ~AL_CPU_RESUME_FLG_PERCPU_DONT_RESUME; 2274d892e4fSZbigniew Bodek bus_space_write_4(fdtbus_bs_tag, cpu_resume_baddr, 2284d892e4fSZbigniew Bodek AL_CPU_RESUME_PCPU_FLAGS(a), val); 2294d892e4fSZbigniew Bodek mb(); 2304d892e4fSZbigniew Bodek 2314d892e4fSZbigniew Bodek /* Set resume physical address */ 2324d892e4fSZbigniew Bodek bus_space_write_4(fdtbus_bs_tag, cpu_resume_baddr, 2334d892e4fSZbigniew Bodek AL_CPU_RESUME_PCPU_RADDR_REG(a), physaddr); 2344d892e4fSZbigniew Bodek mb(); 2354d892e4fSZbigniew Bodek } 2364d892e4fSZbigniew Bodek 2374d892e4fSZbigniew Bodek /* Release cores from reset */ 2384d892e4fSZbigniew Bodek if (bus_space_map(fdtbus_bs_tag, al_devmap_pa + nb_base, 2394d892e4fSZbigniew Bodek nb_size, 0, &nb_baddr)) 2404d892e4fSZbigniew Bodek panic("Couldn't map NB-service area"); 2414d892e4fSZbigniew Bodek 2424d892e4fSZbigniew Bodek start_mask = (1 << platform_mp_get_core_cnt()) - 1; 2434d892e4fSZbigniew Bodek 2444d892e4fSZbigniew Bodek /* Release cores from reset */ 2454d892e4fSZbigniew Bodek val = bus_space_read_4(fdtbus_bs_tag, nb_baddr, AL_NB_INIT_CONTROL); 2464d892e4fSZbigniew Bodek val |= start_mask; 2474d892e4fSZbigniew Bodek bus_space_write_4(fdtbus_bs_tag, nb_baddr, AL_NB_INIT_CONTROL, val); 2484d892e4fSZbigniew Bodek dsb(); 2494d892e4fSZbigniew Bodek 2504d892e4fSZbigniew Bodek bus_space_unmap(fdtbus_bs_tag, nb_baddr, nb_size); 2514d892e4fSZbigniew Bodek bus_space_unmap(fdtbus_bs_tag, cpu_resume_baddr, cpu_resume_size); 2524d892e4fSZbigniew Bodek } 253