1280905Sganbold/*- 2280905Sganbold * Copyright 2015 John Wehle <john@feith.com> 3280905Sganbold * All rights reserved. 4280905Sganbold * 5280905Sganbold * Redistribution and use in source and binary forms, with or without 6280905Sganbold * modification, are permitted provided that the following conditions 7280905Sganbold * are met: 8280905Sganbold * 1. Redistributions of source code must retain the above copyright 9280905Sganbold * notice, this list of conditions and the following disclaimer. 10280905Sganbold * 2. Redistributions in binary form must reproduce the above copyright 11280905Sganbold * notice, this list of conditions and the following disclaimer in the 12280905Sganbold * documentation and/or other materials provided with the distribution. 13280905Sganbold * 14280905Sganbold * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15280905Sganbold * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16280905Sganbold * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17280905Sganbold * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18280905Sganbold * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19280905Sganbold * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20280905Sganbold * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21280905Sganbold * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22280905Sganbold * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23280905Sganbold * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24280905Sganbold * SUCH DAMAGE. 25280905Sganbold */ 26280905Sganbold 27280905Sganbold/* 28280905Sganbold * Amlogic aml8726 multiprocessor support. 29280905Sganbold * 30280905Sganbold * Some processors require powering on which involves poking registers 31280905Sganbold * on the aobus and cbus ... it's expected that these locations are set 32280905Sganbold * in stone. 33280905Sganbold * 34280905Sganbold * Locking is not used as these routines should only be called by the BP 35280905Sganbold * during startup and should complete prior to the APs being released (the 36280905Sganbold * issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG 37280905Sganbold * is not concurrently modified). 38280905Sganbold */ 39280905Sganbold 40280905Sganbold#include <sys/cdefs.h> 41280905Sganbold__FBSDID("$FreeBSD: stable/11/sys/arm/amlogic/aml8726/aml8726_mp.c 307344 2016-10-15 08:27:54Z mmel $"); 42280905Sganbold#include <sys/param.h> 43280905Sganbold#include <sys/systm.h> 44280905Sganbold#include <sys/bus.h> 45280905Sganbold#include <sys/kernel.h> 46280905Sganbold#include <sys/module.h> 47280905Sganbold#include <sys/lock.h> 48280905Sganbold#include <sys/mutex.h> 49280905Sganbold#include <sys/resource.h> 50280905Sganbold#include <sys/rman.h> 51280905Sganbold#include <sys/smp.h> 52280905Sganbold 53281092Sandrew#include <vm/vm.h> 54281092Sandrew#include <vm/pmap.h> 55281092Sandrew 56295319Smmel#include <machine/cpu.h> 57280905Sganbold#include <machine/bus.h> 58280905Sganbold#include <machine/smp.h> 59280905Sganbold#include <machine/fdt.h> 60280905Sganbold#include <machine/intr.h> 61280905Sganbold 62280905Sganbold#include <dev/fdt/fdt_common.h> 63280905Sganbold#include <dev/ofw/ofw_bus.h> 64280905Sganbold#include <dev/ofw/ofw_bus_subr.h> 65280905Sganbold 66280905Sganbold#include <arm/amlogic/aml8726/aml8726_soc.h> 67280905Sganbold 68280905Sganboldstatic const char *scu_compatible[] = { 69280905Sganbold "arm,cortex-a5-scu", 70280905Sganbold "arm,cortex-a9-scu", 71280905Sganbold NULL 72280905Sganbold}; 73280905Sganbold 74280905Sganboldstatic const char *scu_errata_764369[] = { 75280905Sganbold "arm,cortex-a9-scu", 76280905Sganbold NULL 77280905Sganbold}; 78280905Sganbold 79280905Sganboldstatic const char *cpucfg_compatible[] = { 80280905Sganbold "amlogic,aml8726-cpuconfig", 81280905Sganbold NULL 82280905Sganbold}; 83280905Sganbold 84280905Sganboldstatic struct { 85280905Sganbold boolean_t errata_764369; 86280905Sganbold u_long scu_size; 87280905Sganbold struct resource scu_res; 88280905Sganbold u_long cpucfg_size; 89280905Sganbold struct resource cpucfg_res; 90280905Sganbold struct resource aobus_res; 91280905Sganbold struct resource cbus_res; 92280905Sganbold} aml8726_smp; 93280905Sganbold 94280905Sganbold#define AML_SCU_CONTROL_REG 0 95280905Sganbold#define AML_SCU_CONTROL_ENABLE 1 96280905Sganbold#define AML_SCU_CONFIG_REG 4 97280905Sganbold#define AML_SCU_CONFIG_NCPU_MASK 0x3 98280905Sganbold#define AML_SCU_CPU_PWR_STATUS_REG 8 99280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU3_MASK (3 << 24) 100280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU2_MASK (3 << 16) 101280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU1_MASK (3 << 8) 102280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU0_MASK 3 103280905Sganbold#define AML_SCU_INV_TAGS_REG 12 104280905Sganbold#define AML_SCU_DIAG_CONTROL_REG 48 105280905Sganbold#define AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT 1 106280905Sganbold 107280905Sganbold#define AML_CPUCONF_CONTROL_REG 0 108280905Sganbold#define AML_CPUCONF_CPU1_ADDR_REG 4 109280905Sganbold#define AML_CPUCONF_CPU2_ADDR_REG 8 110280905Sganbold#define AML_CPUCONF_CPU3_ADDR_REG 12 111280905Sganbold 112280905Sganbold/* aobus */ 113280905Sganbold 114280905Sganbold#define AML_M8_CPU_PWR_CNTL0_REG 0xe0 115280905Sganbold#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK (3 << 22) 116280905Sganbold#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK (3 << 20) 117280905Sganbold#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK (3 << 18) 118280905Sganbold 119280905Sganbold#define AML_M8_CPU_PWR_CNTL0_ISO_CPU3 (1 << 3) 120280905Sganbold#define AML_M8_CPU_PWR_CNTL0_ISO_CPU2 (1 << 2) 121280905Sganbold#define AML_M8_CPU_PWR_CNTL0_ISO_CPU1 (1 << 1) 122280905Sganbold 123280905Sganbold#define AML_M8_CPU_PWR_CNTL1_REG 0xe4 124280905Sganbold#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU3 (1 << 19) 125280905Sganbold#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU2 (1 << 18) 126280905Sganbold#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 (1 << 17) 127280905Sganbold 128280905Sganbold#define AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK (3 << 8) 129280905Sganbold#define AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK (3 << 6) 130280905Sganbold#define AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK (3 << 4) 131280905Sganbold 132280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_REG 0xf4 133280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_CPU3 (0xf << 20) 134280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_CPU2 (0xf << 24) 135280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_CPU1 (0xf << 28) 136280905Sganbold 137280905Sganbold/* cbus */ 138280905Sganbold 139280905Sganbold#define AML_SOC_CPU_CLK_CNTL_REG 0x419c 140280905Sganbold#define AML_M8_CPU_CLK_CNTL_RESET_CPU3 (1 << 27) 141280905Sganbold#define AML_M8_CPU_CLK_CNTL_RESET_CPU2 (1 << 26) 142280905Sganbold#define AML_M8_CPU_CLK_CNTL_RESET_CPU1 (1 << 25) 143280905Sganbold 144280905Sganbold#define SCU_WRITE_4(reg, value) bus_write_4(&aml8726_smp.scu_res, \ 145280905Sganbold (reg), (value)) 146280905Sganbold#define SCU_READ_4(reg) bus_read_4(&aml8726_smp.scu_res, (reg)) 147280905Sganbold#define SCU_BARRIER(reg) bus_barrier(&aml8726_smp.scu_res, \ 148280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 149280905Sganbold 150280905Sganbold#define CPUCONF_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cpucfg_res, \ 151280905Sganbold (reg), (value)) 152280905Sganbold#define CPUCONF_READ_4(reg) bus_read_4(&aml8726_smp.cpucfg_res, \ 153280905Sganbold (reg)) 154280905Sganbold#define CPUCONF_BARRIER(reg) bus_barrier(&aml8726_smp.cpucfg_res, \ 155280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 156280905Sganbold 157280905Sganbold#define AOBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.aobus_res, \ 158280905Sganbold (reg), (value)) 159280905Sganbold#define AOBUS_READ_4(reg) bus_read_4(&aml8726_smp.aobus_res, \ 160280905Sganbold (reg)) 161280905Sganbold#define AOBUS_BARRIER(reg) bus_barrier(&aml8726_smp.aobus_res, \ 162280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 163280905Sganbold 164280905Sganbold#define CBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cbus_res, \ 165280905Sganbold (reg), (value)) 166280905Sganbold#define CBUS_READ_4(reg) bus_read_4(&aml8726_smp.cbus_res, \ 167280905Sganbold (reg)) 168280905Sganbold#define CBUS_BARRIER(reg) bus_barrier(&aml8726_smp.cbus_res, \ 169280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 170280905Sganbold 171280905Sganboldstatic phandle_t 172280905Sganboldfind_node_for_device(const char *device, const char **compatible) 173280905Sganbold{ 174280905Sganbold int i; 175280905Sganbold phandle_t node; 176280905Sganbold 177280905Sganbold /* 178280905Sganbold * Try to access the node directly i.e. through /aliases/. 179280905Sganbold */ 180280905Sganbold 181280905Sganbold if ((node = OF_finddevice(device)) != 0) 182280905Sganbold for (i = 0; compatible[i]; i++) 183280905Sganbold if (fdt_is_compatible_strict(node, compatible[i])) 184280905Sganbold return node; 185280905Sganbold 186280905Sganbold /* 187280905Sganbold * Find the node the long way. 188280905Sganbold */ 189280905Sganbold 190280905Sganbold for (i = 0; compatible[i]; i++) { 191280905Sganbold if ((node = OF_finddevice("/soc")) == 0) 192280905Sganbold return (0); 193280905Sganbold 194280905Sganbold if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0) 195280905Sganbold return node; 196280905Sganbold } 197280905Sganbold 198280905Sganbold return (0); 199280905Sganbold} 200280905Sganbold 201280905Sganbold 202280905Sganboldstatic int 203280905Sganboldalloc_resource_for_node(phandle_t node, struct resource *res, u_long *size) 204280905Sganbold{ 205280905Sganbold int err; 206280905Sganbold u_long pbase, psize; 207280905Sganbold u_long start; 208280905Sganbold 209280905Sganbold if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 || 210280905Sganbold (err = fdt_regsize(node, &start, size)) != 0) 211280905Sganbold return (err); 212280905Sganbold 213280905Sganbold start += pbase; 214280905Sganbold 215280905Sganbold memset(res, 0, sizeof(*res)); 216280905Sganbold 217280905Sganbold res->r_bustag = fdtbus_bs_tag; 218280905Sganbold 219280905Sganbold err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle); 220280905Sganbold 221280905Sganbold return (err); 222280905Sganbold} 223280905Sganbold 224280905Sganbold 225280905Sganboldstatic void 226280905Sganboldpower_on_cpu(int cpu) 227280905Sganbold{ 228280905Sganbold uint32_t scpsr; 229280905Sganbold uint32_t value; 230280905Sganbold 231280905Sganbold if (cpu <= 0) 232280905Sganbold return; 233280905Sganbold 234280905Sganbold /* 235280905Sganbold * Power on the CPU if the intricate details are known, otherwise 236280905Sganbold * just hope for the best (it may have already be powered on by 237280905Sganbold * the hardware / firmware). 238280905Sganbold */ 239280905Sganbold 240280905Sganbold switch (aml8726_soc_hw_rev) { 241280905Sganbold case AML_SOC_HW_REV_M8: 242280905Sganbold case AML_SOC_HW_REV_M8B: 243280905Sganbold /* 244280905Sganbold * Set the SCU power status for the CPU to normal mode. 245280905Sganbold */ 246280905Sganbold scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG); 247280905Sganbold scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8)); 248280905Sganbold SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr); 249280905Sganbold SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG); 250280905Sganbold 251280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 252280905Sganbold /* 253280905Sganbold * Reset may cause the current power status from the 254280905Sganbold * actual CPU to be written to the SCU (over-writing 255280905Sganbold * the value we've just written) so set it to normal 256280905Sganbold * mode as well. 257280905Sganbold */ 258280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG); 259280905Sganbold value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK << 260280905Sganbold ((cpu - 1) * 2)); 261280905Sganbold AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value); 262280905Sganbold AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG); 263280905Sganbold } 264280905Sganbold 265280905Sganbold DELAY(5); 266280905Sganbold 267280905Sganbold /* 268280905Sganbold * Assert reset. 269280905Sganbold */ 270280905Sganbold value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG); 271280905Sganbold value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1); 272280905Sganbold CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value); 273280905Sganbold CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG); 274280905Sganbold 275280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 276280905Sganbold /* 277280905Sganbold * Release RAM pull-down. 278280905Sganbold */ 279280905Sganbold value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG); 280280905Sganbold value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >> 281280905Sganbold ((cpu - 1) * 4)); 282280905Sganbold AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value); 283280905Sganbold AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG); 284280905Sganbold } 285280905Sganbold 286280905Sganbold /* 287280905Sganbold * Power on CPU. 288280905Sganbold */ 289280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG); 290280905Sganbold value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK << 291280905Sganbold ((cpu - 1) * 2)); 292280905Sganbold AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value); 293280905Sganbold AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG); 294280905Sganbold 295280905Sganbold DELAY(10); 296280905Sganbold 297280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 298280905Sganbold /* 299280905Sganbold * Wait for power on confirmation. 300280905Sganbold */ 301280905Sganbold for ( ; ; ) { 302280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG); 303280905Sganbold value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 << 304280905Sganbold (cpu - 1); 305280905Sganbold if (value) 306280905Sganbold break; 307280905Sganbold DELAY(10); 308280905Sganbold } 309280905Sganbold } 310280905Sganbold 311280905Sganbold /* 312280905Sganbold * Release peripheral clamp. 313280905Sganbold */ 314280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG); 315280905Sganbold value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1)); 316280905Sganbold AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value); 317280905Sganbold AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG); 318280905Sganbold 319280905Sganbold /* 320280905Sganbold * Release reset. 321280905Sganbold */ 322280905Sganbold value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG); 323280905Sganbold value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1)); 324280905Sganbold CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value); 325280905Sganbold CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG); 326280905Sganbold 327280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 328280905Sganbold /* 329280905Sganbold * The Amlogic Linux platform code sets the SCU power 330280905Sganbold * status for the CPU again for some reason so we 331280905Sganbold * follow suit (perhaps in case the reset caused 332280905Sganbold * a stale power status from the actual CPU to be 333280905Sganbold * written to the SCU). 334280905Sganbold */ 335280905Sganbold SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr); 336280905Sganbold SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG); 337280905Sganbold } 338280905Sganbold break; 339280905Sganbold default: 340280905Sganbold break; 341280905Sganbold } 342280905Sganbold} 343280905Sganbold 344280905Sganboldvoid 345280905Sganboldplatform_mp_setmaxid(void) 346280905Sganbold{ 347280905Sganbold int err; 348280905Sganbold int i; 349280905Sganbold int ncpu; 350280905Sganbold phandle_t cpucfg_node; 351280905Sganbold phandle_t scu_node; 352280905Sganbold uint32_t value; 353280905Sganbold 354280905Sganbold if (mp_ncpus != 0) 355280905Sganbold return; 356280905Sganbold 357280905Sganbold ncpu = 1; 358280905Sganbold 359280905Sganbold /* 360280905Sganbold * Is the hardware necessary for SMP present? 361280905Sganbold */ 362280905Sganbold 363280905Sganbold if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0) 364280905Sganbold goto moveon; 365280905Sganbold 366280905Sganbold if ((cpucfg_node = find_node_for_device("cpuconfig", 367280905Sganbold cpucfg_compatible)) == 0) 368280905Sganbold goto moveon; 369280905Sganbold 370280905Sganbold if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res, 371280905Sganbold &aml8726_smp.scu_size) != 0) 372280905Sganbold panic("Could not allocate resource for SCU"); 373280905Sganbold 374280905Sganbold if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res, 375280905Sganbold &aml8726_smp.cpucfg_size) != 0) 376280905Sganbold panic("Could not allocate resource for CPUCONFIG"); 377280905Sganbold 378280905Sganbold 379280905Sganbold /* 380280905Sganbold * Strictly speaking the aobus and cbus may not be required in 381280905Sganbold * order to start an AP (it depends on the processor), however 382280905Sganbold * always mapping them in simplifies the code. 383280905Sganbold */ 384280905Sganbold 385280905Sganbold aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag; 386280905Sganbold 387280905Sganbold err = bus_space_map(aml8726_smp.aobus_res.r_bustag, 388280905Sganbold AML_SOC_AOBUS_BASE_ADDR, 0x100000, 389280905Sganbold 0, &aml8726_smp.aobus_res.r_bushandle); 390280905Sganbold 391280905Sganbold if (err) 392280905Sganbold panic("Could not allocate resource for AOBUS"); 393280905Sganbold 394280905Sganbold aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag; 395280905Sganbold 396280905Sganbold err = bus_space_map(aml8726_smp.cbus_res.r_bustag, 397280905Sganbold AML_SOC_CBUS_BASE_ADDR, 0x100000, 398280905Sganbold 0, &aml8726_smp.cbus_res.r_bushandle); 399280905Sganbold 400280905Sganbold if (err) 401280905Sganbold panic("Could not allocate resource for CBUS"); 402280905Sganbold 403280905Sganbold aml8726_smp.errata_764369 = false; 404280905Sganbold for (i = 0; scu_errata_764369[i]; i++) 405280905Sganbold if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) { 406280905Sganbold aml8726_smp.errata_764369 = true; 407280905Sganbold break; 408280905Sganbold } 409280905Sganbold 410280905Sganbold /* 411280905Sganbold * Read the number of CPUs present. 412280905Sganbold */ 413280905Sganbold value = SCU_READ_4(AML_SCU_CONFIG_REG); 414280905Sganbold ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1; 415280905Sganbold 416280905Sganboldmoveon: 417280905Sganbold mp_ncpus = ncpu; 418280905Sganbold mp_maxid = ncpu - 1; 419280905Sganbold} 420280905Sganbold 421280905Sganboldvoid 422280905Sganboldplatform_mp_start_ap(void) 423280905Sganbold{ 424280905Sganbold int i; 425280905Sganbold uint32_t reg; 426280905Sganbold uint32_t value; 427280905Sganbold vm_paddr_t paddr; 428280905Sganbold 429280905Sganbold if (mp_ncpus < 2) 430280905Sganbold return; 431280905Sganbold 432280905Sganbold /* 433280905Sganbold * Invalidate SCU cache tags. The 0x0000ffff constant invalidates 434280905Sganbold * all ways on all cores 0-3. Per the ARM docs, it's harmless to 435280905Sganbold * write to the bits for cores that are not present. 436280905Sganbold */ 437280905Sganbold SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff); 438280905Sganbold 439280905Sganbold if (aml8726_smp.errata_764369) { 440280905Sganbold /* 441280905Sganbold * Erratum ARM/MP: 764369 (problems with cache maintenance). 442280905Sganbold * Setting the "disable-migratory bit" in the undocumented SCU 443280905Sganbold * Diagnostic Control Register helps work around the problem. 444280905Sganbold */ 445280905Sganbold value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG); 446280905Sganbold value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT; 447280905Sganbold SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value); 448280905Sganbold } 449280905Sganbold 450280905Sganbold /* 451280905Sganbold * Enable the SCU, then clean the cache on this core. After these 452280905Sganbold * two operations the cache tag ram in the SCU is coherent with 453280905Sganbold * the contents of the cache on this core. The other cores aren't 454280905Sganbold * running yet so their caches can't contain valid data yet, however 455280905Sganbold * we've initialized their SCU tag ram above, so they will be 456280905Sganbold * coherent from startup. 457280905Sganbold */ 458280905Sganbold value = SCU_READ_4(AML_SCU_CONTROL_REG); 459280905Sganbold value |= AML_SCU_CONTROL_ENABLE; 460280905Sganbold SCU_WRITE_4(AML_SCU_CONTROL_REG, value); 461280905Sganbold SCU_BARRIER(AML_SCU_CONTROL_REG); 462295319Smmel dcache_wbinv_poc_all(); 463280905Sganbold 464280905Sganbold /* Set the boot address and power on each AP. */ 465280905Sganbold paddr = pmap_kextract((vm_offset_t)mpentry); 466280905Sganbold for (i = 1; i < mp_ncpus; i++) { 467280905Sganbold reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4); 468280905Sganbold CPUCONF_WRITE_4(reg, paddr); 469280905Sganbold CPUCONF_BARRIER(reg); 470280905Sganbold 471280905Sganbold power_on_cpu(i); 472280905Sganbold } 473280905Sganbold 474280905Sganbold /* 475280905Sganbold * Enable the APs. 476280905Sganbold * 477280905Sganbold * The Amlogic Linux platform code sets the lsb for some reason 478280905Sganbold * in addition to the enable bit for each AP so we follow suit 479280905Sganbold * (the lsb may be the enable bit for the BP, though in that case 480280905Sganbold * it should already be set since it's currently running). 481280905Sganbold */ 482280905Sganbold value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG); 483280905Sganbold value |= 1; 484280905Sganbold for (i = 1; i < mp_ncpus; i++) 485280905Sganbold value |= (1 << i); 486280905Sganbold CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value); 487280905Sganbold CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG); 488280905Sganbold 489280905Sganbold /* Wakeup the now enabled APs */ 490307344Smmel dsb(); 491307344Smmel sev(); 492280905Sganbold 493280905Sganbold /* 494280905Sganbold * Free the resources which are not needed after startup. 495280905Sganbold */ 496280905Sganbold bus_space_unmap(aml8726_smp.scu_res.r_bustag, 497280905Sganbold aml8726_smp.scu_res.r_bushandle, 498280905Sganbold aml8726_smp.scu_size); 499280905Sganbold bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag, 500280905Sganbold aml8726_smp.cpucfg_res.r_bushandle, 501280905Sganbold aml8726_smp.cpucfg_size); 502280905Sganbold bus_space_unmap(aml8726_smp.aobus_res.r_bustag, 503280905Sganbold aml8726_smp.aobus_res.r_bushandle, 504280905Sganbold 0x100000); 505280905Sganbold bus_space_unmap(aml8726_smp.cbus_res.r_bustag, 506280905Sganbold aml8726_smp.cbus_res.r_bushandle, 507280905Sganbold 0x100000); 508280905Sganbold memset(&aml8726_smp, 0, sizeof(aml8726_smp)); 509280905Sganbold} 510280905Sganbold 511280905Sganbold/* 512280905Sganbold * Stub drivers for cosmetic purposes. 513280905Sganbold */ 514280905Sganboldstruct aml8726_scu_softc { 515280905Sganbold device_t dev; 516280905Sganbold}; 517280905Sganbold 518280905Sganboldstatic int 519280905Sganboldaml8726_scu_probe(device_t dev) 520280905Sganbold{ 521280905Sganbold int i; 522280905Sganbold 523280905Sganbold for (i = 0; scu_compatible[i]; i++) 524280905Sganbold if (ofw_bus_is_compatible(dev, scu_compatible[i])) 525280905Sganbold break; 526280905Sganbold 527280905Sganbold if (!scu_compatible[i]) 528280905Sganbold return (ENXIO); 529280905Sganbold 530280905Sganbold device_set_desc(dev, "ARM Snoop Control Unit"); 531280905Sganbold 532280905Sganbold return (BUS_PROBE_DEFAULT); 533280905Sganbold} 534280905Sganbold 535280905Sganboldstatic int 536280905Sganboldaml8726_scu_attach(device_t dev) 537280905Sganbold{ 538280905Sganbold struct aml8726_scu_softc *sc = device_get_softc(dev); 539280905Sganbold 540280905Sganbold sc->dev = dev; 541280905Sganbold 542280905Sganbold return (0); 543280905Sganbold} 544280905Sganbold 545280905Sganboldstatic int 546280905Sganboldaml8726_scu_detach(device_t dev) 547280905Sganbold{ 548280905Sganbold 549280905Sganbold return (0); 550280905Sganbold} 551280905Sganbold 552280905Sganboldstatic device_method_t aml8726_scu_methods[] = { 553280905Sganbold /* Device interface */ 554280905Sganbold DEVMETHOD(device_probe, aml8726_scu_probe), 555280905Sganbold DEVMETHOD(device_attach, aml8726_scu_attach), 556280905Sganbold DEVMETHOD(device_detach, aml8726_scu_detach), 557280905Sganbold 558280905Sganbold DEVMETHOD_END 559280905Sganbold}; 560280905Sganbold 561280905Sganboldstatic driver_t aml8726_scu_driver = { 562280905Sganbold "scu", 563280905Sganbold aml8726_scu_methods, 564280905Sganbold sizeof(struct aml8726_scu_softc), 565280905Sganbold}; 566280905Sganbold 567280905Sganboldstatic devclass_t aml8726_scu_devclass; 568280905Sganbold 569280905SganboldEARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass, 570280905Sganbold 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE); 571280905Sganbold 572280905Sganboldstruct aml8726_cpucfg_softc { 573280905Sganbold device_t dev; 574280905Sganbold}; 575280905Sganbold 576280905Sganboldstatic int 577280905Sganboldaml8726_cpucfg_probe(device_t dev) 578280905Sganbold{ 579280905Sganbold int i; 580280905Sganbold 581280905Sganbold for (i = 0; cpucfg_compatible[i]; i++) 582280905Sganbold if (ofw_bus_is_compatible(dev, cpucfg_compatible[i])) 583280905Sganbold break; 584280905Sganbold 585280905Sganbold if (!cpucfg_compatible[i]) 586280905Sganbold return (ENXIO); 587280905Sganbold 588280905Sganbold device_set_desc(dev, "Amlogic CPU Config"); 589280905Sganbold 590280905Sganbold return (BUS_PROBE_DEFAULT); 591280905Sganbold} 592280905Sganbold 593280905Sganboldstatic int 594280905Sganboldaml8726_cpucfg_attach(device_t dev) 595280905Sganbold{ 596280905Sganbold struct aml8726_cpucfg_softc *sc = device_get_softc(dev); 597280905Sganbold 598280905Sganbold sc->dev = dev; 599280905Sganbold 600280905Sganbold return (0); 601280905Sganbold} 602280905Sganbold 603280905Sganboldstatic int 604280905Sganboldaml8726_cpucfg_detach(device_t dev) 605280905Sganbold{ 606280905Sganbold 607280905Sganbold return (0); 608280905Sganbold} 609280905Sganbold 610280905Sganboldstatic device_method_t aml8726_cpucfg_methods[] = { 611280905Sganbold /* Device interface */ 612280905Sganbold DEVMETHOD(device_probe, aml8726_cpucfg_probe), 613280905Sganbold DEVMETHOD(device_attach, aml8726_cpucfg_attach), 614280905Sganbold DEVMETHOD(device_detach, aml8726_cpucfg_detach), 615280905Sganbold 616280905Sganbold DEVMETHOD_END 617280905Sganbold}; 618280905Sganbold 619280905Sganboldstatic driver_t aml8726_cpucfg_driver = { 620280905Sganbold "cpuconfig", 621280905Sganbold aml8726_cpucfg_methods, 622280905Sganbold sizeof(struct aml8726_cpucfg_softc), 623280905Sganbold}; 624280905Sganbold 625280905Sganboldstatic devclass_t aml8726_cpucfg_devclass; 626280905Sganbold 627280905SganboldEARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver, 628280905Sganbold aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE); 629