|
@@ -16,14 +16,37 @@
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
*/
|
|
|
|
|
|
+#include <linux/delay.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/of.h>
|
|
|
#include <linux/smp.h>
|
|
|
|
|
|
#include <asm/cacheflush.h>
|
|
|
#include <asm/cpu_ops.h>
|
|
|
+#include <asm/cputype.h>
|
|
|
+#include <asm/smp_plat.h>
|
|
|
+
|
|
|
+extern void secondary_holding_pen(void);
|
|
|
+volatile unsigned long secondary_holding_pen_release = INVALID_HWID;
|
|
|
|
|
|
static phys_addr_t cpu_release_addr[NR_CPUS];
|
|
|
+static DEFINE_RAW_SPINLOCK(boot_lock);
|
|
|
+
|
|
|
+/*
|
|
|
+ * Write secondary_holding_pen_release in a way that is guaranteed to be
|
|
|
+ * visible to all observers, irrespective of whether they're taking part
|
|
|
+ * in coherency or not. This is necessary for the hotplug code to work
|
|
|
+ * reliably.
|
|
|
+ */
|
|
|
+static void write_pen_release(u64 val)
|
|
|
+{
|
|
|
+ void *start = (void *)&secondary_holding_pen_release;
|
|
|
+ unsigned long size = sizeof(secondary_holding_pen_release);
|
|
|
+
|
|
|
+ secondary_holding_pen_release = val;
|
|
|
+ __flush_dcache_area(start, size);
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu)
|
|
|
{
|
|
@@ -60,8 +83,60 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int smp_spin_table_cpu_boot(unsigned int cpu)
|
|
|
+{
|
|
|
+ unsigned long timeout;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Set synchronisation state between this boot processor
|
|
|
+ * and the secondary one
|
|
|
+ */
|
|
|
+ raw_spin_lock(&boot_lock);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Update the pen release flag.
|
|
|
+ */
|
|
|
+ write_pen_release(cpu_logical_map(cpu));
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Send an event, causing the secondaries to read pen_release.
|
|
|
+ */
|
|
|
+ sev();
|
|
|
+
|
|
|
+ timeout = jiffies + (1 * HZ);
|
|
|
+ while (time_before(jiffies, timeout)) {
|
|
|
+ if (secondary_holding_pen_release == INVALID_HWID)
|
|
|
+ break;
|
|
|
+ udelay(10);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Now the secondary core is starting up let it run its
|
|
|
+ * calibrations, then wait for it to finish
|
|
|
+ */
|
|
|
+ raw_spin_unlock(&boot_lock);
|
|
|
+
|
|
|
+ return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0;
|
|
|
+}
|
|
|
+
|
|
|
+void smp_spin_table_cpu_postboot(void)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * Let the primary processor know we're out of the pen.
|
|
|
+ */
|
|
|
+ write_pen_release(INVALID_HWID);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Synchronise with the boot thread.
|
|
|
+ */
|
|
|
+ raw_spin_lock(&boot_lock);
|
|
|
+ raw_spin_unlock(&boot_lock);
|
|
|
+}
|
|
|
+
|
|
|
const struct cpu_operations smp_spin_table_ops = {
|
|
|
.name = "spin-table",
|
|
|
.cpu_init = smp_spin_table_cpu_init,
|
|
|
.cpu_prepare = smp_spin_table_cpu_prepare,
|
|
|
+ .cpu_boot = smp_spin_table_cpu_boot,
|
|
|
+ .cpu_postboot = smp_spin_table_cpu_postboot,
|
|
|
};
|