1172067e0SAndreas Färber /* 2172067e0SAndreas Färber * Actions Semi Leopard 3172067e0SAndreas Färber * 4172067e0SAndreas Färber * This file is based on arm realview smp platform. 5172067e0SAndreas Färber * 6172067e0SAndreas Färber * Copyright 2012 Actions Semi Inc. 7172067e0SAndreas Färber * Author: Actions Semi, Inc. 8172067e0SAndreas Färber * 9172067e0SAndreas Färber * Copyright (c) 2017 Andreas Färber 10172067e0SAndreas Färber * 11172067e0SAndreas Färber * This program is free software; you can redistribute it and/or modify it 12172067e0SAndreas Färber * under the terms of the GNU General Public License as published by the 13172067e0SAndreas Färber * Free Software Foundation; either version 2 of the License, or (at your 14172067e0SAndreas Färber * option) any later version. 15172067e0SAndreas Färber */ 16172067e0SAndreas Färber 17172067e0SAndreas Färber #include <linux/delay.h> 18172067e0SAndreas Färber #include <linux/io.h> 19172067e0SAndreas Färber #include <linux/of.h> 20172067e0SAndreas Färber #include <linux/of_address.h> 21172067e0SAndreas Färber #include <linux/smp.h> 22b6a0e18cSAndreas Färber #include <linux/soc/actions/owl-sps.h> 23172067e0SAndreas Färber #include <asm/cacheflush.h> 24172067e0SAndreas Färber #include <asm/smp_plat.h> 25172067e0SAndreas Färber #include <asm/smp_scu.h> 26172067e0SAndreas Färber 27172067e0SAndreas Färber #define OWL_CPU1_ADDR 0x50 28172067e0SAndreas Färber #define OWL_CPU1_FLAG 0x5c 29172067e0SAndreas Färber 30172067e0SAndreas Färber #define OWL_CPUx_FLAG_BOOT 0x55aa 31172067e0SAndreas Färber 32b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU2 BIT(5) 33b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU3 BIT(6) 34b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU2 BIT(21) 35b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU3 BIT(22) 36b6a0e18cSAndreas Färber 37172067e0SAndreas Färber static void __iomem *scu_base_addr; 38b6a0e18cSAndreas Färber static void __iomem *sps_base_addr; 39172067e0SAndreas Färber static void __iomem *timer_base_addr; 40172067e0SAndreas Färber static int ncores; 41172067e0SAndreas Färber 42172067e0SAndreas Färber static DEFINE_SPINLOCK(boot_lock); 43172067e0SAndreas Färber 44172067e0SAndreas Färber static void write_pen_release(int val) 45172067e0SAndreas Färber { 46172067e0SAndreas Färber pen_release = val; 47172067e0SAndreas Färber smp_wmb(); 48172067e0SAndreas Färber __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); 49172067e0SAndreas Färber outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); 50172067e0SAndreas Färber } 51172067e0SAndreas Färber 52172067e0SAndreas Färber static void s500_smp_secondary_init(unsigned int cpu) 53172067e0SAndreas Färber { 54172067e0SAndreas Färber /* 55172067e0SAndreas Färber * let the primary processor know we're out of the 56172067e0SAndreas Färber * pen, then head off into the C entry point 57172067e0SAndreas Färber */ 58172067e0SAndreas Färber write_pen_release(-1); 59172067e0SAndreas Färber 60172067e0SAndreas Färber spin_lock(&boot_lock); 61172067e0SAndreas Färber spin_unlock(&boot_lock); 62172067e0SAndreas Färber } 63172067e0SAndreas Färber 64172067e0SAndreas Färber void owl_secondary_startup(void); 65172067e0SAndreas Färber 66172067e0SAndreas Färber static int s500_wakeup_secondary(unsigned int cpu) 67172067e0SAndreas Färber { 68b6a0e18cSAndreas Färber int ret; 69b6a0e18cSAndreas Färber 70172067e0SAndreas Färber if (cpu > 3) 71172067e0SAndreas Färber return -EINVAL; 72172067e0SAndreas Färber 73b6a0e18cSAndreas Färber /* The generic PM domain driver is not available this early. */ 74172067e0SAndreas Färber switch (cpu) { 75172067e0SAndreas Färber case 2: 76b6a0e18cSAndreas Färber ret = owl_sps_set_pg(sps_base_addr, 77b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_PWR_CPU2, 78b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_ACK_CPU2, true); 79b6a0e18cSAndreas Färber if (ret) 80b6a0e18cSAndreas Färber return ret; 81b6a0e18cSAndreas Färber break; 82172067e0SAndreas Färber case 3: 83b6a0e18cSAndreas Färber ret = owl_sps_set_pg(sps_base_addr, 84b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_PWR_CPU3, 85b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_ACK_CPU3, true); 86b6a0e18cSAndreas Färber if (ret) 87b6a0e18cSAndreas Färber return ret; 88b6a0e18cSAndreas Färber break; 89172067e0SAndreas Färber } 90172067e0SAndreas Färber 91172067e0SAndreas Färber /* wait for CPUx to run to WFE instruction */ 92172067e0SAndreas Färber udelay(200); 93172067e0SAndreas Färber 94172067e0SAndreas Färber writel(virt_to_phys(owl_secondary_startup), 95172067e0SAndreas Färber timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 96172067e0SAndreas Färber writel(OWL_CPUx_FLAG_BOOT, 97172067e0SAndreas Färber timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 98172067e0SAndreas Färber 99172067e0SAndreas Färber dsb_sev(); 100172067e0SAndreas Färber mb(); 101172067e0SAndreas Färber 102172067e0SAndreas Färber return 0; 103172067e0SAndreas Färber } 104172067e0SAndreas Färber 105172067e0SAndreas Färber static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle) 106172067e0SAndreas Färber { 107172067e0SAndreas Färber unsigned long timeout; 108172067e0SAndreas Färber int ret; 109172067e0SAndreas Färber 110172067e0SAndreas Färber ret = s500_wakeup_secondary(cpu); 111172067e0SAndreas Färber if (ret) 112172067e0SAndreas Färber return ret; 113172067e0SAndreas Färber 114172067e0SAndreas Färber udelay(10); 115172067e0SAndreas Färber 116172067e0SAndreas Färber spin_lock(&boot_lock); 117172067e0SAndreas Färber 118172067e0SAndreas Färber /* 119172067e0SAndreas Färber * The secondary processor is waiting to be released from 120172067e0SAndreas Färber * the holding pen - release it, then wait for it to flag 121172067e0SAndreas Färber * that it has been released by resetting pen_release. 122172067e0SAndreas Färber */ 123172067e0SAndreas Färber write_pen_release(cpu_logical_map(cpu)); 124172067e0SAndreas Färber smp_send_reschedule(cpu); 125172067e0SAndreas Färber 126172067e0SAndreas Färber timeout = jiffies + (1 * HZ); 127172067e0SAndreas Färber while (time_before(jiffies, timeout)) { 128172067e0SAndreas Färber if (pen_release == -1) 129172067e0SAndreas Färber break; 130172067e0SAndreas Färber } 131172067e0SAndreas Färber 132172067e0SAndreas Färber writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 133172067e0SAndreas Färber writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 134172067e0SAndreas Färber 135172067e0SAndreas Färber spin_unlock(&boot_lock); 136172067e0SAndreas Färber 137172067e0SAndreas Färber return pen_release != -1 ? -ENOSYS : 0; 138172067e0SAndreas Färber } 139172067e0SAndreas Färber 140172067e0SAndreas Färber static void __init s500_smp_prepare_cpus(unsigned int max_cpus) 141172067e0SAndreas Färber { 142172067e0SAndreas Färber struct device_node *node; 143172067e0SAndreas Färber 144172067e0SAndreas Färber node = of_find_compatible_node(NULL, NULL, "actions,s500-timer"); 145172067e0SAndreas Färber if (!node) { 146172067e0SAndreas Färber pr_err("%s: missing timer\n", __func__); 147172067e0SAndreas Färber return; 148172067e0SAndreas Färber } 149172067e0SAndreas Färber 150172067e0SAndreas Färber timer_base_addr = of_iomap(node, 0); 151172067e0SAndreas Färber if (!timer_base_addr) { 152172067e0SAndreas Färber pr_err("%s: could not map timer registers\n", __func__); 153172067e0SAndreas Färber return; 154172067e0SAndreas Färber } 155172067e0SAndreas Färber 156b6a0e18cSAndreas Färber node = of_find_compatible_node(NULL, NULL, "actions,s500-sps"); 157b6a0e18cSAndreas Färber if (!node) { 158b6a0e18cSAndreas Färber pr_err("%s: missing sps\n", __func__); 159b6a0e18cSAndreas Färber return; 160b6a0e18cSAndreas Färber } 161b6a0e18cSAndreas Färber 162b6a0e18cSAndreas Färber sps_base_addr = of_iomap(node, 0); 163b6a0e18cSAndreas Färber if (!sps_base_addr) { 164b6a0e18cSAndreas Färber pr_err("%s: could not map sps registers\n", __func__); 165b6a0e18cSAndreas Färber return; 166b6a0e18cSAndreas Färber } 167b6a0e18cSAndreas Färber 168172067e0SAndreas Färber if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { 169172067e0SAndreas Färber node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 170172067e0SAndreas Färber if (!node) { 171172067e0SAndreas Färber pr_err("%s: missing scu\n", __func__); 172172067e0SAndreas Färber return; 173172067e0SAndreas Färber } 174172067e0SAndreas Färber 175172067e0SAndreas Färber scu_base_addr = of_iomap(node, 0); 176172067e0SAndreas Färber if (!scu_base_addr) { 177172067e0SAndreas Färber pr_err("%s: could not map scu registers\n", __func__); 178172067e0SAndreas Färber return; 179172067e0SAndreas Färber } 180172067e0SAndreas Färber 181172067e0SAndreas Färber /* 182172067e0SAndreas Färber * While the number of cpus is gathered from dt, also get the 183172067e0SAndreas Färber * number of cores from the scu to verify this value when 184172067e0SAndreas Färber * booting the cores. 185172067e0SAndreas Färber */ 186172067e0SAndreas Färber ncores = scu_get_core_count(scu_base_addr); 187172067e0SAndreas Färber pr_debug("%s: ncores %d\n", __func__, ncores); 188172067e0SAndreas Färber 189172067e0SAndreas Färber scu_enable(scu_base_addr); 190172067e0SAndreas Färber } 191172067e0SAndreas Färber } 192172067e0SAndreas Färber 193172067e0SAndreas Färber static const struct smp_operations s500_smp_ops __initconst = { 194172067e0SAndreas Färber .smp_prepare_cpus = s500_smp_prepare_cpus, 195172067e0SAndreas Färber .smp_secondary_init = s500_smp_secondary_init, 196172067e0SAndreas Färber .smp_boot_secondary = s500_smp_boot_secondary, 197172067e0SAndreas Färber }; 198172067e0SAndreas Färber CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops); 199