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