1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Actions Semi Leopard 4 * 5 * This file is based on arm realview smp platform. 6 * 7 * Copyright 2012 Actions Semi Inc. 8 * Author: Actions Semi, Inc. 9 * 10 * Copyright (c) 2017 Andreas Färber 11 */ 12 13 #include <linux/delay.h> 14 #include <linux/io.h> 15 #include <linux/of.h> 16 #include <linux/of_address.h> 17 #include <linux/smp.h> 18 #include <linux/soc/actions/owl-sps.h> 19 #include <asm/cacheflush.h> 20 #include <asm/smp_plat.h> 21 #include <asm/smp_scu.h> 22 23 #include <trace/events/ipi.h> 24 25 #define OWL_CPU1_ADDR 0x50 26 #define OWL_CPU1_FLAG 0x5c 27 28 #define OWL_CPUx_FLAG_BOOT 0x55aa 29 30 #define OWL_SPS_PG_CTL_PWR_CPU2 BIT(5) 31 #define OWL_SPS_PG_CTL_PWR_CPU3 BIT(6) 32 #define OWL_SPS_PG_CTL_ACK_CPU2 BIT(21) 33 #define OWL_SPS_PG_CTL_ACK_CPU3 BIT(22) 34 35 static void __iomem *scu_base_addr; 36 static void __iomem *sps_base_addr; 37 static void __iomem *timer_base_addr; 38 static int ncores; 39 40 static int s500_wakeup_secondary(unsigned int cpu) 41 { 42 int ret; 43 44 if (cpu > 3) 45 return -EINVAL; 46 47 /* The generic PM domain driver is not available this early. */ 48 switch (cpu) { 49 case 2: 50 ret = owl_sps_set_pg(sps_base_addr, 51 OWL_SPS_PG_CTL_PWR_CPU2, 52 OWL_SPS_PG_CTL_ACK_CPU2, true); 53 if (ret) 54 return ret; 55 break; 56 case 3: 57 ret = owl_sps_set_pg(sps_base_addr, 58 OWL_SPS_PG_CTL_PWR_CPU3, 59 OWL_SPS_PG_CTL_ACK_CPU3, true); 60 if (ret) 61 return ret; 62 break; 63 } 64 65 /* wait for CPUx to run to WFE instruction */ 66 udelay(200); 67 68 writel(__pa_symbol(secondary_startup), 69 timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 70 writel(OWL_CPUx_FLAG_BOOT, 71 timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 72 73 dsb_sev(); 74 mb(); 75 76 return 0; 77 } 78 79 static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle) 80 { 81 int ret; 82 83 ret = s500_wakeup_secondary(cpu); 84 if (ret) 85 return ret; 86 87 udelay(10); 88 89 smp_send_reschedule(cpu); 90 91 writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 92 writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 93 94 return 0; 95 } 96 97 static void __init s500_smp_prepare_cpus(unsigned int max_cpus) 98 { 99 struct device_node *node; 100 101 node = of_find_compatible_node(NULL, NULL, "actions,s500-timer"); 102 if (!node) { 103 pr_err("%s: missing timer\n", __func__); 104 return; 105 } 106 107 timer_base_addr = of_iomap(node, 0); 108 if (!timer_base_addr) { 109 pr_err("%s: could not map timer registers\n", __func__); 110 return; 111 } 112 113 node = of_find_compatible_node(NULL, NULL, "actions,s500-sps"); 114 if (!node) { 115 pr_err("%s: missing sps\n", __func__); 116 return; 117 } 118 119 sps_base_addr = of_iomap(node, 0); 120 if (!sps_base_addr) { 121 pr_err("%s: could not map sps registers\n", __func__); 122 return; 123 } 124 125 if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { 126 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 127 if (!node) { 128 pr_err("%s: missing scu\n", __func__); 129 return; 130 } 131 132 scu_base_addr = of_iomap(node, 0); 133 if (!scu_base_addr) { 134 pr_err("%s: could not map scu registers\n", __func__); 135 return; 136 } 137 138 /* 139 * While the number of cpus is gathered from dt, also get the 140 * number of cores from the scu to verify this value when 141 * booting the cores. 142 */ 143 ncores = scu_get_core_count(scu_base_addr); 144 pr_debug("%s: ncores %d\n", __func__, ncores); 145 146 scu_enable(scu_base_addr); 147 } 148 } 149 150 static const struct smp_operations s500_smp_ops __initconst = { 151 .smp_prepare_cpus = s500_smp_prepare_cpus, 152 .smp_boot_secondary = s500_smp_boot_secondary, 153 }; 154 CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops); 155
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.