162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Actions Semi Leopard 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file is based on arm realview smp platform. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2012 Actions Semi Inc. 862306a36Sopenharmony_ci * Author: Actions Semi, Inc. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (c) 2017 Andreas Färber 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/smp.h> 1862306a36Sopenharmony_ci#include <linux/soc/actions/owl-sps.h> 1962306a36Sopenharmony_ci#include <asm/cacheflush.h> 2062306a36Sopenharmony_ci#include <asm/smp_plat.h> 2162306a36Sopenharmony_ci#include <asm/smp_scu.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <trace/events/ipi.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define OWL_CPU1_ADDR 0x50 2662306a36Sopenharmony_ci#define OWL_CPU1_FLAG 0x5c 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define OWL_CPUx_FLAG_BOOT 0x55aa 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define OWL_SPS_PG_CTL_PWR_CPU2 BIT(5) 3162306a36Sopenharmony_ci#define OWL_SPS_PG_CTL_PWR_CPU3 BIT(6) 3262306a36Sopenharmony_ci#define OWL_SPS_PG_CTL_ACK_CPU2 BIT(21) 3362306a36Sopenharmony_ci#define OWL_SPS_PG_CTL_ACK_CPU3 BIT(22) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void __iomem *scu_base_addr; 3662306a36Sopenharmony_cistatic void __iomem *sps_base_addr; 3762306a36Sopenharmony_cistatic void __iomem *timer_base_addr; 3862306a36Sopenharmony_cistatic int ncores; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int s500_wakeup_secondary(unsigned int cpu) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci int ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (cpu > 3) 4562306a36Sopenharmony_ci return -EINVAL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* The generic PM domain driver is not available this early. */ 4862306a36Sopenharmony_ci switch (cpu) { 4962306a36Sopenharmony_ci case 2: 5062306a36Sopenharmony_ci ret = owl_sps_set_pg(sps_base_addr, 5162306a36Sopenharmony_ci OWL_SPS_PG_CTL_PWR_CPU2, 5262306a36Sopenharmony_ci OWL_SPS_PG_CTL_ACK_CPU2, true); 5362306a36Sopenharmony_ci if (ret) 5462306a36Sopenharmony_ci return ret; 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case 3: 5762306a36Sopenharmony_ci ret = owl_sps_set_pg(sps_base_addr, 5862306a36Sopenharmony_ci OWL_SPS_PG_CTL_PWR_CPU3, 5962306a36Sopenharmony_ci OWL_SPS_PG_CTL_ACK_CPU3, true); 6062306a36Sopenharmony_ci if (ret) 6162306a36Sopenharmony_ci return ret; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* wait for CPUx to run to WFE instruction */ 6662306a36Sopenharmony_ci udelay(200); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci writel(__pa_symbol(secondary_startup), 6962306a36Sopenharmony_ci timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 7062306a36Sopenharmony_ci writel(OWL_CPUx_FLAG_BOOT, 7162306a36Sopenharmony_ci timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci dsb_sev(); 7462306a36Sopenharmony_ci mb(); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int ret; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ret = s500_wakeup_secondary(cpu); 8462306a36Sopenharmony_ci if (ret) 8562306a36Sopenharmony_ci return ret; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci udelay(10); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci smp_send_reschedule(cpu); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 9262306a36Sopenharmony_ci writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void __init s500_smp_prepare_cpus(unsigned int max_cpus) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct device_node *node; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "actions,s500-timer"); 10262306a36Sopenharmony_ci if (!node) { 10362306a36Sopenharmony_ci pr_err("%s: missing timer\n", __func__); 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci timer_base_addr = of_iomap(node, 0); 10862306a36Sopenharmony_ci if (!timer_base_addr) { 10962306a36Sopenharmony_ci pr_err("%s: could not map timer registers\n", __func__); 11062306a36Sopenharmony_ci return; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "actions,s500-sps"); 11462306a36Sopenharmony_ci if (!node) { 11562306a36Sopenharmony_ci pr_err("%s: missing sps\n", __func__); 11662306a36Sopenharmony_ci return; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci sps_base_addr = of_iomap(node, 0); 12062306a36Sopenharmony_ci if (!sps_base_addr) { 12162306a36Sopenharmony_ci pr_err("%s: could not map sps registers\n", __func__); 12262306a36Sopenharmony_ci return; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { 12662306a36Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 12762306a36Sopenharmony_ci if (!node) { 12862306a36Sopenharmony_ci pr_err("%s: missing scu\n", __func__); 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci scu_base_addr = of_iomap(node, 0); 13362306a36Sopenharmony_ci if (!scu_base_addr) { 13462306a36Sopenharmony_ci pr_err("%s: could not map scu registers\n", __func__); 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* 13962306a36Sopenharmony_ci * While the number of cpus is gathered from dt, also get the 14062306a36Sopenharmony_ci * number of cores from the scu to verify this value when 14162306a36Sopenharmony_ci * booting the cores. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci ncores = scu_get_core_count(scu_base_addr); 14462306a36Sopenharmony_ci pr_debug("%s: ncores %d\n", __func__, ncores); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci scu_enable(scu_base_addr); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic const struct smp_operations s500_smp_ops __initconst = { 15162306a36Sopenharmony_ci .smp_prepare_cpus = s500_smp_prepare_cpus, 15262306a36Sopenharmony_ci .smp_boot_secondary = s500_smp_boot_secondary, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops); 155