platsmp.c 2.9 KB
Newer Older
A
Antoine Ténart 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) 2014 Marvell Technology Group Ltd.
 *
 * Antoine Ténart <antoine.tenart@free-electrons.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/io.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_address.h>

#include <asm/cacheflush.h>
17
#include <asm/cp15.h>
A
Antoine Ténart 已提交
18 19 20
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>

21 22 23 24 25 26
/*
 * There are two reset registers, one with self-clearing (SC)
 * reset and one with non-self-clearing reset (NON_SC).
 */
#define CPU_RESET_SC		0x00
#define CPU_RESET_NON_SC	0x20
A
Antoine Ténart 已提交
27 28 29 30 31 32 33 34 35 36 37 38

#define RESET_VECT		0x00
#define SW_RESET_ADDR		0x94

extern u32 boot_inst;

static void __iomem *cpu_ctrl;

static inline void berlin_perform_reset_cpu(unsigned int cpu)
{
	u32 val;

39 40 41
	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
	val &= ~BIT(cpu_logical_map(cpu));
	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
A
Antoine Ténart 已提交
42
	val |= BIT(cpu_logical_map(cpu));
43
	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
A
Antoine Ténart 已提交
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
}

static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	if (!cpu_ctrl)
		return -EFAULT;

	/*
	 * Reset the CPU, making it to execute the instruction in the reset
	 * exception vector.
	 */
	berlin_perform_reset_cpu(cpu);

	return 0;
}

static void __init berlin_smp_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *np;
	void __iomem *scu_base;
	void __iomem *vectors_base;

	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
	scu_base = of_iomap(np, 0);
	of_node_put(np);
	if (!scu_base)
		return;

	np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl");
	cpu_ctrl = of_iomap(np, 0);
	of_node_put(np);
	if (!cpu_ctrl)
		goto unmap_scu;

	vectors_base = ioremap(CONFIG_VECTORS_BASE, SZ_32K);
	if (!vectors_base)
		goto unmap_scu;

	scu_enable(scu_base);
	flush_cache_all();

	/*
	 * Write the first instruction the CPU will execute after being reset
	 * in the reset exception vector.
	 */
	writel(boot_inst, vectors_base + RESET_VECT);

	/*
	 * Write the secondary startup address into the SW reset address
	 * vector. This is used by boot_inst.
	 */
95
	writel(virt_to_phys(secondary_startup), vectors_base + SW_RESET_ADDR);
A
Antoine Ténart 已提交
96 97 98 99 100 101

	iounmap(vectors_base);
unmap_scu:
	iounmap(scu_base);
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
#ifdef CONFIG_HOTPLUG_CPU
static void berlin_cpu_die(unsigned int cpu)
{
	v7_exit_coherency_flush(louis);
	while (1)
		cpu_do_idle();
}

static int berlin_cpu_kill(unsigned int cpu)
{
	u32 val;

	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
	val &= ~BIT(cpu_logical_map(cpu));
	writel(val, cpu_ctrl + CPU_RESET_NON_SC);

	return 1;
}
#endif

122
static const struct smp_operations berlin_smp_ops __initconst = {
A
Antoine Ténart 已提交
123 124
	.smp_prepare_cpus	= berlin_smp_prepare_cpus,
	.smp_boot_secondary	= berlin_boot_secondary,
125 126 127 128
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= berlin_cpu_die,
	.cpu_kill		= berlin_cpu_kill,
#endif
A
Antoine Ténart 已提交
129 130
};
CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops);