aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-sunxi/platsmp.c
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2013-11-03 04:30:13 -0500
committerMaxime Ripard <maxime.ripard@free-electrons.com>2013-12-16 15:33:41 -0500
commit73346794b48237b7b4c2a5c02b12e3c4f1da7551 (patch)
tree49e6ef08466d94f7899e4beea8bb4cc7991b3f75 /arch/arm/mach-sunxi/platsmp.c
parenta3b7a0c84d56bc50d33428f302778104b7164ba2 (diff)
ARM: sun6i: Add SMP support for the Allwinner A31
The A31 is a quad Cortex-A7. Add the logic to use the IPs used to control the CPU configuration and the CPU power so that we can bring up secondary CPUs at boot. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Diffstat (limited to 'arch/arm/mach-sunxi/platsmp.c')
-rw-r--r--arch/arm/mach-sunxi/platsmp.c124
1 files changed, 124 insertions, 0 deletions
diff --git a/arch/arm/mach-sunxi/platsmp.c b/arch/arm/mach-sunxi/platsmp.c
new file mode 100644
index 000000000000..7b141d8342a1
--- /dev/null
+++ b/arch/arm/mach-sunxi/platsmp.c
@@ -0,0 +1,124 @@
1/*
2 * SMP support for Allwinner SoCs
3 *
4 * Copyright (C) 2013 Maxime Ripard
5 *
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 *
8 * Based on code
9 * Copyright (C) 2012-2013 Allwinner Ltd.
10 *
11 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied.
14 */
15
16#include <linux/delay.h>
17#include <linux/init.h>
18#include <linux/io.h>
19#include <linux/memory.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/smp.h>
23
24#include "common.h"
25
26#define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu) ((cpu) * 0x40 + 0x64)
27#define CPUCFG_CPU_RST_CTRL_REG(cpu) (((cpu) + 1) * 0x40)
28#define CPUCFG_CPU_CTRL_REG(cpu) (((cpu) + 1) * 0x40 + 0x04)
29#define CPUCFG_CPU_STATUS_REG(cpu) (((cpu) + 1) * 0x40 + 0x08)
30#define CPUCFG_GEN_CTRL_REG 0x184
31#define CPUCFG_PRIVATE0_REG 0x1a4
32#define CPUCFG_PRIVATE1_REG 0x1a8
33#define CPUCFG_DBG_CTL0_REG 0x1e0
34#define CPUCFG_DBG_CTL1_REG 0x1e4
35
36#define PRCM_CPU_PWROFF_REG 0x100
37#define PRCM_CPU_PWR_CLAMP_REG(cpu) (((cpu) * 4) + 0x140)
38
39static void __iomem *cpucfg_membase;
40static void __iomem *prcm_membase;
41
42static DEFINE_SPINLOCK(cpu_lock);
43
44static void __init sun6i_smp_prepare_cpus(unsigned int max_cpus)
45{
46 struct device_node *node;
47
48 node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm");
49 if (!node) {
50 pr_err("Missing A31 PRCM node in the device tree\n");
51 return;
52 }
53
54 prcm_membase = of_iomap(node, 0);
55 if (!prcm_membase) {
56 pr_err("Couldn't map A31 PRCM registers\n");
57 return;
58 }
59
60 node = of_find_compatible_node(NULL, NULL,
61 "allwinner,sun6i-a31-cpuconfig");
62 if (!node) {
63 pr_err("Missing A31 CPU config node in the device tree\n");
64 return;
65 }
66
67 cpucfg_membase = of_iomap(node, 0);
68 if (!cpucfg_membase)
69 pr_err("Couldn't map A31 CPU config registers\n");
70
71}
72
73static int sun6i_smp_boot_secondary(unsigned int cpu,
74 struct task_struct *idle)
75{
76 u32 reg;
77 int i;
78
79 if (!(prcm_membase && cpucfg_membase))
80 return -EFAULT;
81
82 spin_lock(&cpu_lock);
83
84 /* Set CPU boot address */
85 writel(virt_to_phys(sun6i_secondary_startup),
86 cpucfg_membase + CPUCFG_PRIVATE0_REG);
87
88 /* Assert the CPU core in reset */
89 writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
90
91 /* Assert the L1 cache in reset */
92 reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
93 writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
94
95 /* Disable external debug access */
96 reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
97 writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
98
99 /* Power up the CPU */
100 for (i = 0; i <= 8; i++)
101 writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu));
102 mdelay(10);
103
104 /* Clear CPU power-off gating */
105 reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
106 writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
107 mdelay(1);
108
109 /* Deassert the CPU core reset */
110 writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
111
112 /* Enable back the external debug accesses */
113 reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
114 writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
115
116 spin_unlock(&cpu_lock);
117
118 return 0;
119}
120
121struct smp_operations sun6i_smp_ops __initdata = {
122 .smp_prepare_cpus = sun6i_smp_prepare_cpus,
123 .smp_boot_secondary = sun6i_smp_boot_secondary,
124};