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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | /* * 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 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #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 DEFINE_SPINLOCK(boot_lock); void owl_secondary_startup(void); 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(virt_to_phys(owl_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) { unsigned long timeout; int ret; ret = s500_wakeup_secondary(cpu); if (ret) return ret; udelay(10); spin_lock(&boot_lock); smp_send_reschedule(cpu); timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { if (pen_release == -1) break; } writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); spin_unlock(&boot_lock); 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); |