platsmp.c (3474B)
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#define OWL_CPU1_ADDR 0x50 24#define OWL_CPU1_FLAG 0x5c 25 26#define OWL_CPUx_FLAG_BOOT 0x55aa 27 28#define OWL_SPS_PG_CTL_PWR_CPU2 BIT(5) 29#define OWL_SPS_PG_CTL_PWR_CPU3 BIT(6) 30#define OWL_SPS_PG_CTL_ACK_CPU2 BIT(21) 31#define OWL_SPS_PG_CTL_ACK_CPU3 BIT(22) 32 33static void __iomem *scu_base_addr; 34static void __iomem *sps_base_addr; 35static void __iomem *timer_base_addr; 36static int ncores; 37 38static int s500_wakeup_secondary(unsigned int cpu) 39{ 40 int ret; 41 42 if (cpu > 3) 43 return -EINVAL; 44 45 /* The generic PM domain driver is not available this early. */ 46 switch (cpu) { 47 case 2: 48 ret = owl_sps_set_pg(sps_base_addr, 49 OWL_SPS_PG_CTL_PWR_CPU2, 50 OWL_SPS_PG_CTL_ACK_CPU2, true); 51 if (ret) 52 return ret; 53 break; 54 case 3: 55 ret = owl_sps_set_pg(sps_base_addr, 56 OWL_SPS_PG_CTL_PWR_CPU3, 57 OWL_SPS_PG_CTL_ACK_CPU3, true); 58 if (ret) 59 return ret; 60 break; 61 } 62 63 /* wait for CPUx to run to WFE instruction */ 64 udelay(200); 65 66 writel(__pa_symbol(secondary_startup), 67 timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 68 writel(OWL_CPUx_FLAG_BOOT, 69 timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 70 71 dsb_sev(); 72 mb(); 73 74 return 0; 75} 76 77static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle) 78{ 79 int ret; 80 81 ret = s500_wakeup_secondary(cpu); 82 if (ret) 83 return ret; 84 85 udelay(10); 86 87 smp_send_reschedule(cpu); 88 89 writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); 90 writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); 91 92 return 0; 93} 94 95static void __init s500_smp_prepare_cpus(unsigned int max_cpus) 96{ 97 struct device_node *node; 98 99 node = of_find_compatible_node(NULL, NULL, "actions,s500-timer"); 100 if (!node) { 101 pr_err("%s: missing timer\n", __func__); 102 return; 103 } 104 105 timer_base_addr = of_iomap(node, 0); 106 if (!timer_base_addr) { 107 pr_err("%s: could not map timer registers\n", __func__); 108 return; 109 } 110 111 node = of_find_compatible_node(NULL, NULL, "actions,s500-sps"); 112 if (!node) { 113 pr_err("%s: missing sps\n", __func__); 114 return; 115 } 116 117 sps_base_addr = of_iomap(node, 0); 118 if (!sps_base_addr) { 119 pr_err("%s: could not map sps registers\n", __func__); 120 return; 121 } 122 123 if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { 124 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 125 if (!node) { 126 pr_err("%s: missing scu\n", __func__); 127 return; 128 } 129 130 scu_base_addr = of_iomap(node, 0); 131 if (!scu_base_addr) { 132 pr_err("%s: could not map scu registers\n", __func__); 133 return; 134 } 135 136 /* 137 * While the number of cpus is gathered from dt, also get the 138 * number of cores from the scu to verify this value when 139 * booting the cores. 140 */ 141 ncores = scu_get_core_count(scu_base_addr); 142 pr_debug("%s: ncores %d\n", __func__, ncores); 143 144 scu_enable(scu_base_addr); 145 } 146} 147 148static const struct smp_operations s500_smp_ops __initconst = { 149 .smp_prepare_cpus = s500_smp_prepare_cpus, 150 .smp_boot_secondary = s500_smp_boot_secondary, 151}; 152CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);