aml8726_mp.c revision 281092
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: head/sys/arm/amlogic/aml8726/aml8726_mp.c 281092 2015-04-04 23:03:11Z andrew $"); 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 56280905Sganbold#include <machine/bus.h> 57280905Sganbold#include <machine/smp.h> 58280905Sganbold#include <machine/fdt.h> 59280905Sganbold#include <machine/intr.h> 60280905Sganbold 61280905Sganbold#include <dev/fdt/fdt_common.h> 62280905Sganbold#include <dev/ofw/ofw_bus.h> 63280905Sganbold#include <dev/ofw/ofw_bus_subr.h> 64280905Sganbold 65280905Sganbold#include <arm/amlogic/aml8726/aml8726_soc.h> 66280905Sganbold 67280905Sganboldstatic const char *scu_compatible[] = { 68280905Sganbold "arm,cortex-a5-scu", 69280905Sganbold "arm,cortex-a9-scu", 70280905Sganbold NULL 71280905Sganbold}; 72280905Sganbold 73280905Sganboldstatic const char *scu_errata_764369[] = { 74280905Sganbold "arm,cortex-a9-scu", 75280905Sganbold NULL 76280905Sganbold}; 77280905Sganbold 78280905Sganboldstatic const char *cpucfg_compatible[] = { 79280905Sganbold "amlogic,aml8726-cpuconfig", 80280905Sganbold NULL 81280905Sganbold}; 82280905Sganbold 83280905Sganboldstatic struct { 84280905Sganbold boolean_t errata_764369; 85280905Sganbold u_long scu_size; 86280905Sganbold struct resource scu_res; 87280905Sganbold u_long cpucfg_size; 88280905Sganbold struct resource cpucfg_res; 89280905Sganbold struct resource aobus_res; 90280905Sganbold struct resource cbus_res; 91280905Sganbold} aml8726_smp; 92280905Sganbold 93280905Sganbold#define AML_SCU_CONTROL_REG 0 94280905Sganbold#define AML_SCU_CONTROL_ENABLE 1 95280905Sganbold#define AML_SCU_CONFIG_REG 4 96280905Sganbold#define AML_SCU_CONFIG_NCPU_MASK 0x3 97280905Sganbold#define AML_SCU_CPU_PWR_STATUS_REG 8 98280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU3_MASK (3 << 24) 99280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU2_MASK (3 << 16) 100280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU1_MASK (3 << 8) 101280905Sganbold#define AML_SCU_CPU_PWR_STATUS_CPU0_MASK 3 102280905Sganbold#define AML_SCU_INV_TAGS_REG 12 103280905Sganbold#define AML_SCU_DIAG_CONTROL_REG 48 104280905Sganbold#define AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT 1 105280905Sganbold 106280905Sganbold#define AML_CPUCONF_CONTROL_REG 0 107280905Sganbold#define AML_CPUCONF_CPU1_ADDR_REG 4 108280905Sganbold#define AML_CPUCONF_CPU2_ADDR_REG 8 109280905Sganbold#define AML_CPUCONF_CPU3_ADDR_REG 12 110280905Sganbold 111280905Sganbold/* aobus */ 112280905Sganbold 113280905Sganbold#define AML_M8_CPU_PWR_CNTL0_REG 0xe0 114280905Sganbold#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK (3 << 22) 115280905Sganbold#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK (3 << 20) 116280905Sganbold#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK (3 << 18) 117280905Sganbold 118280905Sganbold#define AML_M8_CPU_PWR_CNTL0_ISO_CPU3 (1 << 3) 119280905Sganbold#define AML_M8_CPU_PWR_CNTL0_ISO_CPU2 (1 << 2) 120280905Sganbold#define AML_M8_CPU_PWR_CNTL0_ISO_CPU1 (1 << 1) 121280905Sganbold 122280905Sganbold#define AML_M8_CPU_PWR_CNTL1_REG 0xe4 123280905Sganbold#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU3 (1 << 19) 124280905Sganbold#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU2 (1 << 18) 125280905Sganbold#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 (1 << 17) 126280905Sganbold 127280905Sganbold#define AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK (3 << 8) 128280905Sganbold#define AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK (3 << 6) 129280905Sganbold#define AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK (3 << 4) 130280905Sganbold 131280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_REG 0xf4 132280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_CPU3 (0xf << 20) 133280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_CPU2 (0xf << 24) 134280905Sganbold#define AML_M8B_CPU_PWR_MEM_PD0_CPU1 (0xf << 28) 135280905Sganbold 136280905Sganbold/* cbus */ 137280905Sganbold 138280905Sganbold#define AML_SOC_CPU_CLK_CNTL_REG 0x419c 139280905Sganbold#define AML_M8_CPU_CLK_CNTL_RESET_CPU3 (1 << 27) 140280905Sganbold#define AML_M8_CPU_CLK_CNTL_RESET_CPU2 (1 << 26) 141280905Sganbold#define AML_M8_CPU_CLK_CNTL_RESET_CPU1 (1 << 25) 142280905Sganbold 143280905Sganbold#define SCU_WRITE_4(reg, value) bus_write_4(&aml8726_smp.scu_res, \ 144280905Sganbold (reg), (value)) 145280905Sganbold#define SCU_READ_4(reg) bus_read_4(&aml8726_smp.scu_res, (reg)) 146280905Sganbold#define SCU_BARRIER(reg) bus_barrier(&aml8726_smp.scu_res, \ 147280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 148280905Sganbold 149280905Sganbold#define CPUCONF_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cpucfg_res, \ 150280905Sganbold (reg), (value)) 151280905Sganbold#define CPUCONF_READ_4(reg) bus_read_4(&aml8726_smp.cpucfg_res, \ 152280905Sganbold (reg)) 153280905Sganbold#define CPUCONF_BARRIER(reg) bus_barrier(&aml8726_smp.cpucfg_res, \ 154280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 155280905Sganbold 156280905Sganbold#define AOBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.aobus_res, \ 157280905Sganbold (reg), (value)) 158280905Sganbold#define AOBUS_READ_4(reg) bus_read_4(&aml8726_smp.aobus_res, \ 159280905Sganbold (reg)) 160280905Sganbold#define AOBUS_BARRIER(reg) bus_barrier(&aml8726_smp.aobus_res, \ 161280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 162280905Sganbold 163280905Sganbold#define CBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cbus_res, \ 164280905Sganbold (reg), (value)) 165280905Sganbold#define CBUS_READ_4(reg) bus_read_4(&aml8726_smp.cbus_res, \ 166280905Sganbold (reg)) 167280905Sganbold#define CBUS_BARRIER(reg) bus_barrier(&aml8726_smp.cbus_res, \ 168280905Sganbold (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 169280905Sganbold 170280905Sganboldstatic phandle_t 171280905Sganboldfind_node_for_device(const char *device, const char **compatible) 172280905Sganbold{ 173280905Sganbold int i; 174280905Sganbold phandle_t node; 175280905Sganbold 176280905Sganbold /* 177280905Sganbold * Try to access the node directly i.e. through /aliases/. 178280905Sganbold */ 179280905Sganbold 180280905Sganbold if ((node = OF_finddevice(device)) != 0) 181280905Sganbold for (i = 0; compatible[i]; i++) 182280905Sganbold if (fdt_is_compatible_strict(node, compatible[i])) 183280905Sganbold return node; 184280905Sganbold 185280905Sganbold /* 186280905Sganbold * Find the node the long way. 187280905Sganbold */ 188280905Sganbold 189280905Sganbold for (i = 0; compatible[i]; i++) { 190280905Sganbold if ((node = OF_finddevice("/soc")) == 0) 191280905Sganbold return (0); 192280905Sganbold 193280905Sganbold if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0) 194280905Sganbold return node; 195280905Sganbold } 196280905Sganbold 197280905Sganbold return (0); 198280905Sganbold} 199280905Sganbold 200280905Sganbold 201280905Sganboldstatic int 202280905Sganboldalloc_resource_for_node(phandle_t node, struct resource *res, u_long *size) 203280905Sganbold{ 204280905Sganbold int err; 205280905Sganbold u_long pbase, psize; 206280905Sganbold u_long start; 207280905Sganbold 208280905Sganbold if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 || 209280905Sganbold (err = fdt_regsize(node, &start, size)) != 0) 210280905Sganbold return (err); 211280905Sganbold 212280905Sganbold start += pbase; 213280905Sganbold 214280905Sganbold memset(res, 0, sizeof(*res)); 215280905Sganbold 216280905Sganbold res->r_bustag = fdtbus_bs_tag; 217280905Sganbold 218280905Sganbold err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle); 219280905Sganbold 220280905Sganbold return (err); 221280905Sganbold} 222280905Sganbold 223280905Sganbold 224280905Sganboldstatic void 225280905Sganboldpower_on_cpu(int cpu) 226280905Sganbold{ 227280905Sganbold uint32_t scpsr; 228280905Sganbold uint32_t value; 229280905Sganbold 230280905Sganbold if (cpu <= 0) 231280905Sganbold return; 232280905Sganbold 233280905Sganbold /* 234280905Sganbold * Power on the CPU if the intricate details are known, otherwise 235280905Sganbold * just hope for the best (it may have already be powered on by 236280905Sganbold * the hardware / firmware). 237280905Sganbold */ 238280905Sganbold 239280905Sganbold switch (aml8726_soc_hw_rev) { 240280905Sganbold case AML_SOC_HW_REV_M8: 241280905Sganbold case AML_SOC_HW_REV_M8B: 242280905Sganbold /* 243280905Sganbold * Set the SCU power status for the CPU to normal mode. 244280905Sganbold */ 245280905Sganbold scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG); 246280905Sganbold scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8)); 247280905Sganbold SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr); 248280905Sganbold SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG); 249280905Sganbold 250280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 251280905Sganbold /* 252280905Sganbold * Reset may cause the current power status from the 253280905Sganbold * actual CPU to be written to the SCU (over-writing 254280905Sganbold * the value we've just written) so set it to normal 255280905Sganbold * mode as well. 256280905Sganbold */ 257280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG); 258280905Sganbold value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK << 259280905Sganbold ((cpu - 1) * 2)); 260280905Sganbold AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value); 261280905Sganbold AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG); 262280905Sganbold } 263280905Sganbold 264280905Sganbold DELAY(5); 265280905Sganbold 266280905Sganbold /* 267280905Sganbold * Assert reset. 268280905Sganbold */ 269280905Sganbold value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG); 270280905Sganbold value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1); 271280905Sganbold CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value); 272280905Sganbold CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG); 273280905Sganbold 274280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 275280905Sganbold /* 276280905Sganbold * Release RAM pull-down. 277280905Sganbold */ 278280905Sganbold value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG); 279280905Sganbold value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >> 280280905Sganbold ((cpu - 1) * 4)); 281280905Sganbold AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value); 282280905Sganbold AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG); 283280905Sganbold } 284280905Sganbold 285280905Sganbold /* 286280905Sganbold * Power on CPU. 287280905Sganbold */ 288280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG); 289280905Sganbold value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK << 290280905Sganbold ((cpu - 1) * 2)); 291280905Sganbold AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value); 292280905Sganbold AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG); 293280905Sganbold 294280905Sganbold DELAY(10); 295280905Sganbold 296280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 297280905Sganbold /* 298280905Sganbold * Wait for power on confirmation. 299280905Sganbold */ 300280905Sganbold for ( ; ; ) { 301280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG); 302280905Sganbold value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 << 303280905Sganbold (cpu - 1); 304280905Sganbold if (value) 305280905Sganbold break; 306280905Sganbold DELAY(10); 307280905Sganbold } 308280905Sganbold } 309280905Sganbold 310280905Sganbold /* 311280905Sganbold * Release peripheral clamp. 312280905Sganbold */ 313280905Sganbold value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG); 314280905Sganbold value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1)); 315280905Sganbold AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value); 316280905Sganbold AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG); 317280905Sganbold 318280905Sganbold /* 319280905Sganbold * Release reset. 320280905Sganbold */ 321280905Sganbold value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG); 322280905Sganbold value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1)); 323280905Sganbold CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value); 324280905Sganbold CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG); 325280905Sganbold 326280905Sganbold if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { 327280905Sganbold /* 328280905Sganbold * The Amlogic Linux platform code sets the SCU power 329280905Sganbold * status for the CPU again for some reason so we 330280905Sganbold * follow suit (perhaps in case the reset caused 331280905Sganbold * a stale power status from the actual CPU to be 332280905Sganbold * written to the SCU). 333280905Sganbold */ 334280905Sganbold SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr); 335280905Sganbold SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG); 336280905Sganbold } 337280905Sganbold break; 338280905Sganbold default: 339280905Sganbold break; 340280905Sganbold } 341280905Sganbold} 342280905Sganbold 343280905Sganbold 344280905Sganboldvoid 345280905Sganboldplatform_mp_init_secondary(void) 346280905Sganbold{ 347280905Sganbold 348280905Sganbold /* 349280905Sganbold * Consider modifying the timer driver to support 350280905Sganbold * per-cpu timers and then enabling the timer for 351280905Sganbold * each AP. 352280905Sganbold */ 353280905Sganbold 354280905Sganbold arm_init_secondary_ic(); 355280905Sganbold} 356280905Sganbold 357280905Sganbold 358280905Sganboldvoid 359280905Sganboldplatform_mp_setmaxid(void) 360280905Sganbold{ 361280905Sganbold int err; 362280905Sganbold int i; 363280905Sganbold int ncpu; 364280905Sganbold phandle_t cpucfg_node; 365280905Sganbold phandle_t scu_node; 366280905Sganbold uint32_t value; 367280905Sganbold 368280905Sganbold if (mp_ncpus != 0) 369280905Sganbold return; 370280905Sganbold 371280905Sganbold ncpu = 1; 372280905Sganbold 373280905Sganbold /* 374280905Sganbold * Is the hardware necessary for SMP present? 375280905Sganbold */ 376280905Sganbold 377280905Sganbold if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0) 378280905Sganbold goto moveon; 379280905Sganbold 380280905Sganbold if ((cpucfg_node = find_node_for_device("cpuconfig", 381280905Sganbold cpucfg_compatible)) == 0) 382280905Sganbold goto moveon; 383280905Sganbold 384280905Sganbold if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res, 385280905Sganbold &aml8726_smp.scu_size) != 0) 386280905Sganbold panic("Could not allocate resource for SCU"); 387280905Sganbold 388280905Sganbold if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res, 389280905Sganbold &aml8726_smp.cpucfg_size) != 0) 390280905Sganbold panic("Could not allocate resource for CPUCONFIG"); 391280905Sganbold 392280905Sganbold 393280905Sganbold /* 394280905Sganbold * Strictly speaking the aobus and cbus may not be required in 395280905Sganbold * order to start an AP (it depends on the processor), however 396280905Sganbold * always mapping them in simplifies the code. 397280905Sganbold */ 398280905Sganbold 399280905Sganbold aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag; 400280905Sganbold 401280905Sganbold err = bus_space_map(aml8726_smp.aobus_res.r_bustag, 402280905Sganbold AML_SOC_AOBUS_BASE_ADDR, 0x100000, 403280905Sganbold 0, &aml8726_smp.aobus_res.r_bushandle); 404280905Sganbold 405280905Sganbold if (err) 406280905Sganbold panic("Could not allocate resource for AOBUS"); 407280905Sganbold 408280905Sganbold aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag; 409280905Sganbold 410280905Sganbold err = bus_space_map(aml8726_smp.cbus_res.r_bustag, 411280905Sganbold AML_SOC_CBUS_BASE_ADDR, 0x100000, 412280905Sganbold 0, &aml8726_smp.cbus_res.r_bushandle); 413280905Sganbold 414280905Sganbold if (err) 415280905Sganbold panic("Could not allocate resource for CBUS"); 416280905Sganbold 417280905Sganbold aml8726_smp.errata_764369 = false; 418280905Sganbold for (i = 0; scu_errata_764369[i]; i++) 419280905Sganbold if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) { 420280905Sganbold aml8726_smp.errata_764369 = true; 421280905Sganbold break; 422280905Sganbold } 423280905Sganbold 424280905Sganbold /* 425280905Sganbold * Read the number of CPUs present. 426280905Sganbold */ 427280905Sganbold value = SCU_READ_4(AML_SCU_CONFIG_REG); 428280905Sganbold ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1; 429280905Sganbold 430280905Sganboldmoveon: 431280905Sganbold mp_ncpus = ncpu; 432280905Sganbold mp_maxid = ncpu - 1; 433280905Sganbold} 434280905Sganbold 435280905Sganbold 436280905Sganboldint 437280905Sganboldplatform_mp_probe(void) 438280905Sganbold{ 439280905Sganbold 440280905Sganbold if (mp_ncpus == 0) 441280905Sganbold platform_mp_setmaxid(); 442280905Sganbold 443280905Sganbold return (mp_ncpus > 1); 444280905Sganbold} 445280905Sganbold 446280905Sganbold 447280905Sganboldvoid 448280905Sganboldplatform_mp_start_ap(void) 449280905Sganbold{ 450280905Sganbold int i; 451280905Sganbold uint32_t reg; 452280905Sganbold uint32_t value; 453280905Sganbold vm_paddr_t paddr; 454280905Sganbold 455280905Sganbold if (mp_ncpus < 2) 456280905Sganbold return; 457280905Sganbold 458280905Sganbold /* 459280905Sganbold * Invalidate SCU cache tags. The 0x0000ffff constant invalidates 460280905Sganbold * all ways on all cores 0-3. Per the ARM docs, it's harmless to 461280905Sganbold * write to the bits for cores that are not present. 462280905Sganbold */ 463280905Sganbold SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff); 464280905Sganbold 465280905Sganbold if (aml8726_smp.errata_764369) { 466280905Sganbold /* 467280905Sganbold * Erratum ARM/MP: 764369 (problems with cache maintenance). 468280905Sganbold * Setting the "disable-migratory bit" in the undocumented SCU 469280905Sganbold * Diagnostic Control Register helps work around the problem. 470280905Sganbold */ 471280905Sganbold value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG); 472280905Sganbold value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT; 473280905Sganbold SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value); 474280905Sganbold } 475280905Sganbold 476280905Sganbold /* 477280905Sganbold * Enable the SCU, then clean the cache on this core. After these 478280905Sganbold * two operations the cache tag ram in the SCU is coherent with 479280905Sganbold * the contents of the cache on this core. The other cores aren't 480280905Sganbold * running yet so their caches can't contain valid data yet, however 481280905Sganbold * we've initialized their SCU tag ram above, so they will be 482280905Sganbold * coherent from startup. 483280905Sganbold */ 484280905Sganbold value = SCU_READ_4(AML_SCU_CONTROL_REG); 485280905Sganbold value |= AML_SCU_CONTROL_ENABLE; 486280905Sganbold SCU_WRITE_4(AML_SCU_CONTROL_REG, value); 487280905Sganbold SCU_BARRIER(AML_SCU_CONTROL_REG); 488280905Sganbold cpu_idcache_wbinv_all(); 489280905Sganbold 490280905Sganbold /* Set the boot address and power on each AP. */ 491280905Sganbold paddr = pmap_kextract((vm_offset_t)mpentry); 492280905Sganbold for (i = 1; i < mp_ncpus; i++) { 493280905Sganbold reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4); 494280905Sganbold CPUCONF_WRITE_4(reg, paddr); 495280905Sganbold CPUCONF_BARRIER(reg); 496280905Sganbold 497280905Sganbold power_on_cpu(i); 498280905Sganbold } 499280905Sganbold 500280905Sganbold /* 501280905Sganbold * Enable the APs. 502280905Sganbold * 503280905Sganbold * The Amlogic Linux platform code sets the lsb for some reason 504280905Sganbold * in addition to the enable bit for each AP so we follow suit 505280905Sganbold * (the lsb may be the enable bit for the BP, though in that case 506280905Sganbold * it should already be set since it's currently running). 507280905Sganbold */ 508280905Sganbold value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG); 509280905Sganbold value |= 1; 510280905Sganbold for (i = 1; i < mp_ncpus; i++) 511280905Sganbold value |= (1 << i); 512280905Sganbold CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value); 513280905Sganbold CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG); 514280905Sganbold 515280905Sganbold /* Wakeup the now enabled APs */ 516280905Sganbold armv7_sev(); 517280905Sganbold 518280905Sganbold /* 519280905Sganbold * Free the resources which are not needed after startup. 520280905Sganbold */ 521280905Sganbold bus_space_unmap(aml8726_smp.scu_res.r_bustag, 522280905Sganbold aml8726_smp.scu_res.r_bushandle, 523280905Sganbold aml8726_smp.scu_size); 524280905Sganbold bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag, 525280905Sganbold aml8726_smp.cpucfg_res.r_bushandle, 526280905Sganbold aml8726_smp.cpucfg_size); 527280905Sganbold bus_space_unmap(aml8726_smp.aobus_res.r_bustag, 528280905Sganbold aml8726_smp.aobus_res.r_bushandle, 529280905Sganbold 0x100000); 530280905Sganbold bus_space_unmap(aml8726_smp.cbus_res.r_bustag, 531280905Sganbold aml8726_smp.cbus_res.r_bushandle, 532280905Sganbold 0x100000); 533280905Sganbold memset(&aml8726_smp, 0, sizeof(aml8726_smp)); 534280905Sganbold} 535280905Sganbold 536280905Sganboldvoid 537280905Sganboldplatform_ipi_send(cpuset_t cpus, u_int ipi) 538280905Sganbold{ 539280905Sganbold 540280905Sganbold pic_ipi_send(cpus, ipi); 541280905Sganbold} 542280905Sganbold 543280905Sganbold/* 544280905Sganbold * Stub drivers for cosmetic purposes. 545280905Sganbold */ 546280905Sganboldstruct aml8726_scu_softc { 547280905Sganbold device_t dev; 548280905Sganbold}; 549280905Sganbold 550280905Sganboldstatic int 551280905Sganboldaml8726_scu_probe(device_t dev) 552280905Sganbold{ 553280905Sganbold int i; 554280905Sganbold 555280905Sganbold for (i = 0; scu_compatible[i]; i++) 556280905Sganbold if (ofw_bus_is_compatible(dev, scu_compatible[i])) 557280905Sganbold break; 558280905Sganbold 559280905Sganbold if (!scu_compatible[i]) 560280905Sganbold return (ENXIO); 561280905Sganbold 562280905Sganbold device_set_desc(dev, "ARM Snoop Control Unit"); 563280905Sganbold 564280905Sganbold return (BUS_PROBE_DEFAULT); 565280905Sganbold} 566280905Sganbold 567280905Sganboldstatic int 568280905Sganboldaml8726_scu_attach(device_t dev) 569280905Sganbold{ 570280905Sganbold struct aml8726_scu_softc *sc = device_get_softc(dev); 571280905Sganbold 572280905Sganbold sc->dev = dev; 573280905Sganbold 574280905Sganbold return (0); 575280905Sganbold} 576280905Sganbold 577280905Sganboldstatic int 578280905Sganboldaml8726_scu_detach(device_t dev) 579280905Sganbold{ 580280905Sganbold 581280905Sganbold return (0); 582280905Sganbold} 583280905Sganbold 584280905Sganboldstatic device_method_t aml8726_scu_methods[] = { 585280905Sganbold /* Device interface */ 586280905Sganbold DEVMETHOD(device_probe, aml8726_scu_probe), 587280905Sganbold DEVMETHOD(device_attach, aml8726_scu_attach), 588280905Sganbold DEVMETHOD(device_detach, aml8726_scu_detach), 589280905Sganbold 590280905Sganbold DEVMETHOD_END 591280905Sganbold}; 592280905Sganbold 593280905Sganboldstatic driver_t aml8726_scu_driver = { 594280905Sganbold "scu", 595280905Sganbold aml8726_scu_methods, 596280905Sganbold sizeof(struct aml8726_scu_softc), 597280905Sganbold}; 598280905Sganbold 599280905Sganboldstatic devclass_t aml8726_scu_devclass; 600280905Sganbold 601280905SganboldEARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass, 602280905Sganbold 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE); 603280905Sganbold 604280905Sganboldstruct aml8726_cpucfg_softc { 605280905Sganbold device_t dev; 606280905Sganbold}; 607280905Sganbold 608280905Sganboldstatic int 609280905Sganboldaml8726_cpucfg_probe(device_t dev) 610280905Sganbold{ 611280905Sganbold int i; 612280905Sganbold 613280905Sganbold for (i = 0; cpucfg_compatible[i]; i++) 614280905Sganbold if (ofw_bus_is_compatible(dev, cpucfg_compatible[i])) 615280905Sganbold break; 616280905Sganbold 617280905Sganbold if (!cpucfg_compatible[i]) 618280905Sganbold return (ENXIO); 619280905Sganbold 620280905Sganbold device_set_desc(dev, "Amlogic CPU Config"); 621280905Sganbold 622280905Sganbold return (BUS_PROBE_DEFAULT); 623280905Sganbold} 624280905Sganbold 625280905Sganboldstatic int 626280905Sganboldaml8726_cpucfg_attach(device_t dev) 627280905Sganbold{ 628280905Sganbold struct aml8726_cpucfg_softc *sc = device_get_softc(dev); 629280905Sganbold 630280905Sganbold sc->dev = dev; 631280905Sganbold 632280905Sganbold return (0); 633280905Sganbold} 634280905Sganbold 635280905Sganboldstatic int 636280905Sganboldaml8726_cpucfg_detach(device_t dev) 637280905Sganbold{ 638280905Sganbold 639280905Sganbold return (0); 640280905Sganbold} 641280905Sganbold 642280905Sganboldstatic device_method_t aml8726_cpucfg_methods[] = { 643280905Sganbold /* Device interface */ 644280905Sganbold DEVMETHOD(device_probe, aml8726_cpucfg_probe), 645280905Sganbold DEVMETHOD(device_attach, aml8726_cpucfg_attach), 646280905Sganbold DEVMETHOD(device_detach, aml8726_cpucfg_detach), 647280905Sganbold 648280905Sganbold DEVMETHOD_END 649280905Sganbold}; 650280905Sganbold 651280905Sganboldstatic driver_t aml8726_cpucfg_driver = { 652280905Sganbold "cpuconfig", 653280905Sganbold aml8726_cpucfg_methods, 654280905Sganbold sizeof(struct aml8726_cpucfg_softc), 655280905Sganbold}; 656280905Sganbold 657280905Sganboldstatic devclass_t aml8726_cpucfg_devclass; 658280905Sganbold 659280905SganboldEARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver, 660280905Sganbold aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE); 661