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