162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014-2015 Broadcom Corporation 462306a36Sopenharmony_ci * Copyright 2014 Linaro Limited 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/cpumask.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/irqchip/irq-bcm2836.h> 1362306a36Sopenharmony_ci#include <linux/jiffies.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci#include <linux/sched.h> 1762306a36Sopenharmony_ci#include <linux/sched/clock.h> 1862306a36Sopenharmony_ci#include <linux/smp.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/cacheflush.h> 2162306a36Sopenharmony_ci#include <asm/smp.h> 2262306a36Sopenharmony_ci#include <asm/smp_plat.h> 2362306a36Sopenharmony_ci#include <asm/smp_scu.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "platsmp.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Size of mapped Cortex A9 SCU address space */ 2862306a36Sopenharmony_ci#define CORTEX_A9_SCU_SIZE 0x58 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define SECONDARY_TIMEOUT_NS NSEC_PER_MSEC /* 1 msec (in nanoseconds) */ 3162306a36Sopenharmony_ci#define BOOT_ADDR_CPUID_MASK 0x3 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Name of device node property defining secondary boot register location */ 3462306a36Sopenharmony_ci#define OF_SECONDARY_BOOT "secondary-boot-reg" 3562306a36Sopenharmony_ci#define MPIDR_CPUID_BITMASK 0x3 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Enable the Cortex A9 Snoop Control Unit 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * By the time this is called we already know there are multiple 4162306a36Sopenharmony_ci * cores present. We assume we're running on a Cortex A9 processor, 4262306a36Sopenharmony_ci * so any trouble getting the base address register or getting the 4362306a36Sopenharmony_ci * SCU base is a problem. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Return 0 if successful or an error code otherwise. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic int __init scu_a9_enable(void) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci unsigned long config_base; 5062306a36Sopenharmony_ci void __iomem *scu_base; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!scu_a9_has_base()) { 5362306a36Sopenharmony_ci pr_err("no configuration base address register!\n"); 5462306a36Sopenharmony_ci return -ENXIO; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Config base address register value is zero for uniprocessor */ 5862306a36Sopenharmony_ci config_base = scu_a9_get_base(); 5962306a36Sopenharmony_ci if (!config_base) { 6062306a36Sopenharmony_ci pr_err("hardware reports only one core\n"); 6162306a36Sopenharmony_ci return -ENOENT; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE); 6562306a36Sopenharmony_ci if (!scu_base) { 6662306a36Sopenharmony_ci pr_err("failed to remap config base (%lu/%u) for SCU\n", 6762306a36Sopenharmony_ci config_base, CORTEX_A9_SCU_SIZE); 6862306a36Sopenharmony_ci return -ENOMEM; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci scu_enable(scu_base); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci iounmap(scu_base); /* That's the last we'll need of this */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic u32 secondary_boot_addr_for(unsigned int cpu) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci u32 secondary_boot_addr = 0; 8162306a36Sopenharmony_ci struct device_node *cpu_node = of_get_cpu_node(cpu, NULL); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!cpu_node) { 8462306a36Sopenharmony_ci pr_err("Failed to find device tree node for CPU%u\n", cpu); 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (of_property_read_u32(cpu_node, 8962306a36Sopenharmony_ci OF_SECONDARY_BOOT, 9062306a36Sopenharmony_ci &secondary_boot_addr)) 9162306a36Sopenharmony_ci pr_err("required secondary boot register not specified for CPU%u\n", 9262306a36Sopenharmony_ci cpu); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci of_node_put(cpu_node); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return secondary_boot_addr; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int nsp_write_lut(unsigned int cpu) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci void __iomem *sku_rom_lut; 10262306a36Sopenharmony_ci phys_addr_t secondary_startup_phy; 10362306a36Sopenharmony_ci const u32 secondary_boot_addr = secondary_boot_addr_for(cpu); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!secondary_boot_addr) 10662306a36Sopenharmony_ci return -EINVAL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci sku_rom_lut = ioremap((phys_addr_t)secondary_boot_addr, 10962306a36Sopenharmony_ci sizeof(phys_addr_t)); 11062306a36Sopenharmony_ci if (!sku_rom_lut) { 11162306a36Sopenharmony_ci pr_warn("unable to ioremap SKU-ROM LUT register for cpu %u\n", cpu); 11262306a36Sopenharmony_ci return -ENOMEM; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci secondary_startup_phy = __pa_symbol(secondary_startup); 11662306a36Sopenharmony_ci BUG_ON(secondary_startup_phy > (phys_addr_t)U32_MAX); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci writel_relaxed(secondary_startup_phy, sku_rom_lut); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Ensure the write is visible to the secondary core */ 12162306a36Sopenharmony_ci smp_wmb(); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci iounmap(sku_rom_lut); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void __init bcm_smp_prepare_cpus(unsigned int max_cpus) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci const cpumask_t only_cpu_0 = { CPU_BITS_CPU0 }; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Enable the SCU on Cortex A9 based SoCs */ 13362306a36Sopenharmony_ci if (scu_a9_enable()) { 13462306a36Sopenharmony_ci /* Update the CPU present map to reflect uniprocessor mode */ 13562306a36Sopenharmony_ci pr_warn("failed to enable A9 SCU - disabling SMP\n"); 13662306a36Sopenharmony_ci init_cpu_present(&only_cpu_0); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * The ROM code has the secondary cores looping, waiting for an event. 14262306a36Sopenharmony_ci * When an event occurs each core examines the bottom two bits of the 14362306a36Sopenharmony_ci * secondary boot register. When a core finds those bits contain its 14462306a36Sopenharmony_ci * own core id, it performs initialization, including computing its boot 14562306a36Sopenharmony_ci * address by clearing the boot register value's bottom two bits. The 14662306a36Sopenharmony_ci * core signals that it is beginning its execution by writing its boot 14762306a36Sopenharmony_ci * address back to the secondary boot register, and finally jumps to 14862306a36Sopenharmony_ci * that address. 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * So to start a core executing we need to: 15162306a36Sopenharmony_ci * - Encode the (hardware) CPU id with the bottom bits of the secondary 15262306a36Sopenharmony_ci * start address. 15362306a36Sopenharmony_ci * - Write that value into the secondary boot register. 15462306a36Sopenharmony_ci * - Generate an event to wake up the secondary CPU(s). 15562306a36Sopenharmony_ci * - Wait for the secondary boot register to be re-written, which 15662306a36Sopenharmony_ci * indicates the secondary core has started. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic int kona_boot_secondary(unsigned int cpu, struct task_struct *idle) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci void __iomem *boot_reg; 16162306a36Sopenharmony_ci phys_addr_t boot_func; 16262306a36Sopenharmony_ci u64 start_clock; 16362306a36Sopenharmony_ci u32 cpu_id; 16462306a36Sopenharmony_ci u32 boot_val; 16562306a36Sopenharmony_ci bool timeout = false; 16662306a36Sopenharmony_ci const u32 secondary_boot_addr = secondary_boot_addr_for(cpu); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci cpu_id = cpu_logical_map(cpu); 16962306a36Sopenharmony_ci if (cpu_id & ~BOOT_ADDR_CPUID_MASK) { 17062306a36Sopenharmony_ci pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK); 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!secondary_boot_addr) 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci boot_reg = ioremap((phys_addr_t)secondary_boot_addr, 17862306a36Sopenharmony_ci sizeof(phys_addr_t)); 17962306a36Sopenharmony_ci if (!boot_reg) { 18062306a36Sopenharmony_ci pr_err("unable to map boot register for cpu %u\n", cpu_id); 18162306a36Sopenharmony_ci return -ENOMEM; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * Secondary cores will start in secondary_startup(), 18662306a36Sopenharmony_ci * defined in "arch/arm/kernel/head.S" 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci boot_func = __pa_symbol(secondary_startup); 18962306a36Sopenharmony_ci BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK); 19062306a36Sopenharmony_ci BUG_ON(boot_func > (phys_addr_t)U32_MAX); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* The core to start is encoded in the low bits */ 19362306a36Sopenharmony_ci boot_val = (u32)boot_func | cpu_id; 19462306a36Sopenharmony_ci writel_relaxed(boot_val, boot_reg); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci sev(); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* The low bits will be cleared once the core has started */ 19962306a36Sopenharmony_ci start_clock = local_clock(); 20062306a36Sopenharmony_ci while (!timeout && readl_relaxed(boot_reg) == boot_val) 20162306a36Sopenharmony_ci timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci iounmap(boot_reg); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!timeout) 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci pr_err("timeout waiting for cpu %u to start\n", cpu_id); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return -ENXIO; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* Cluster Dormant Control command to bring CPU into a running state */ 21462306a36Sopenharmony_ci#define CDC_CMD 6 21562306a36Sopenharmony_ci#define CDC_CMD_OFFSET 0 21662306a36Sopenharmony_ci#define CDC_CMD_REG(cpu) (CDC_CMD_OFFSET + 4*(cpu)) 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * BCM23550 has a Cluster Dormant Control block that keeps the core in 22062306a36Sopenharmony_ci * idle state. A command needs to be sent to the block to bring the CPU 22162306a36Sopenharmony_ci * into running state. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_cistatic int bcm23550_boot_secondary(unsigned int cpu, struct task_struct *idle) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci void __iomem *cdc_base; 22662306a36Sopenharmony_ci struct device_node *dn; 22762306a36Sopenharmony_ci char *name; 22862306a36Sopenharmony_ci int ret; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Make sure a CDC node exists before booting the 23162306a36Sopenharmony_ci * secondary core. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci name = "brcm,bcm23550-cdc"; 23462306a36Sopenharmony_ci dn = of_find_compatible_node(NULL, NULL, name); 23562306a36Sopenharmony_ci if (!dn) { 23662306a36Sopenharmony_ci pr_err("unable to find cdc node\n"); 23762306a36Sopenharmony_ci return -ENODEV; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci cdc_base = of_iomap(dn, 0); 24162306a36Sopenharmony_ci of_node_put(dn); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!cdc_base) { 24462306a36Sopenharmony_ci pr_err("unable to remap cdc base register\n"); 24562306a36Sopenharmony_ci return -ENOMEM; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Boot the secondary core */ 24962306a36Sopenharmony_ci ret = kona_boot_secondary(cpu, idle); 25062306a36Sopenharmony_ci if (ret) 25162306a36Sopenharmony_ci goto out; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Bring this CPU to RUN state so that nIRQ nFIQ 25462306a36Sopenharmony_ci * signals are unblocked. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci writel_relaxed(CDC_CMD, cdc_base + CDC_CMD_REG(cpu)); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciout: 25962306a36Sopenharmony_ci iounmap(cdc_base); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int nsp_boot_secondary(unsigned int cpu, struct task_struct *idle) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci int ret; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * After wake up, secondary core branches to the startup 27062306a36Sopenharmony_ci * address programmed at SKU ROM LUT location. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci ret = nsp_write_lut(cpu); 27362306a36Sopenharmony_ci if (ret) { 27462306a36Sopenharmony_ci pr_err("unable to write startup addr to SKU ROM LUT\n"); 27562306a36Sopenharmony_ci goto out; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Send a CPU wakeup interrupt to the secondary core */ 27962306a36Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciout: 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int bcm2836_boot_secondary(unsigned int cpu, struct task_struct *idle) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci void __iomem *intc_base; 28862306a36Sopenharmony_ci struct device_node *dn; 28962306a36Sopenharmony_ci char *name; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci name = "brcm,bcm2836-l1-intc"; 29262306a36Sopenharmony_ci dn = of_find_compatible_node(NULL, NULL, name); 29362306a36Sopenharmony_ci if (!dn) { 29462306a36Sopenharmony_ci pr_err("unable to find intc node\n"); 29562306a36Sopenharmony_ci return -ENODEV; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci intc_base = of_iomap(dn, 0); 29962306a36Sopenharmony_ci of_node_put(dn); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (!intc_base) { 30262306a36Sopenharmony_ci pr_err("unable to remap intc base register\n"); 30362306a36Sopenharmony_ci return -ENOMEM; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci writel(virt_to_phys(secondary_startup), 30762306a36Sopenharmony_ci intc_base + LOCAL_MAILBOX3_SET0 + 16 * cpu); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci dsb(sy); 31062306a36Sopenharmony_ci sev(); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci iounmap(intc_base); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic const struct smp_operations kona_smp_ops __initconst = { 31862306a36Sopenharmony_ci .smp_prepare_cpus = bcm_smp_prepare_cpus, 31962306a36Sopenharmony_ci .smp_boot_secondary = kona_boot_secondary, 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method", 32262306a36Sopenharmony_ci &kona_smp_ops); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic const struct smp_operations bcm23550_smp_ops __initconst = { 32562306a36Sopenharmony_ci .smp_boot_secondary = bcm23550_boot_secondary, 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_bcm23550, "brcm,bcm23550", 32862306a36Sopenharmony_ci &bcm23550_smp_ops); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const struct smp_operations nsp_smp_ops __initconst = { 33162306a36Sopenharmony_ci .smp_prepare_cpus = bcm_smp_prepare_cpus, 33262306a36Sopenharmony_ci .smp_boot_secondary = nsp_boot_secondary, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_nsp, "brcm,bcm-nsp-smp", &nsp_smp_ops); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ciconst struct smp_operations bcm2836_smp_ops __initconst = { 33762306a36Sopenharmony_ci .smp_boot_secondary = bcm2836_boot_secondary, 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_bcm2836, "brcm,bcm2836-smp", &bcm2836_smp_ops); 340