Commit | Line | Data |
---|---|---|
73346794 MR |
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 | ||
73346794 MR |
24 | #define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu) ((cpu) * 0x40 + 0x64) |
25 | #define CPUCFG_CPU_RST_CTRL_REG(cpu) (((cpu) + 1) * 0x40) | |
26 | #define CPUCFG_CPU_CTRL_REG(cpu) (((cpu) + 1) * 0x40 + 0x04) | |
27 | #define CPUCFG_CPU_STATUS_REG(cpu) (((cpu) + 1) * 0x40 + 0x08) | |
28 | #define CPUCFG_GEN_CTRL_REG 0x184 | |
29 | #define CPUCFG_PRIVATE0_REG 0x1a4 | |
30 | #define CPUCFG_PRIVATE1_REG 0x1a8 | |
31 | #define CPUCFG_DBG_CTL0_REG 0x1e0 | |
32 | #define CPUCFG_DBG_CTL1_REG 0x1e4 | |
33 | ||
34 | #define PRCM_CPU_PWROFF_REG 0x100 | |
35 | #define PRCM_CPU_PWR_CLAMP_REG(cpu) (((cpu) * 4) + 0x140) | |
36 | ||
37 | static void __iomem *cpucfg_membase; | |
38 | static void __iomem *prcm_membase; | |
39 | ||
40 | static DEFINE_SPINLOCK(cpu_lock); | |
41 | ||
42 | static void __init sun6i_smp_prepare_cpus(unsigned int max_cpus) | |
43 | { | |
44 | struct device_node *node; | |
45 | ||
46 | node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm"); | |
47 | if (!node) { | |
48 | pr_err("Missing A31 PRCM node in the device tree\n"); | |
49 | return; | |
50 | } | |
51 | ||
52 | prcm_membase = of_iomap(node, 0); | |
53 | if (!prcm_membase) { | |
54 | pr_err("Couldn't map A31 PRCM registers\n"); | |
55 | return; | |
56 | } | |
57 | ||
58 | node = of_find_compatible_node(NULL, NULL, | |
59 | "allwinner,sun6i-a31-cpuconfig"); | |
60 | if (!node) { | |
61 | pr_err("Missing A31 CPU config node in the device tree\n"); | |
62 | return; | |
63 | } | |
64 | ||
65 | cpucfg_membase = of_iomap(node, 0); | |
66 | if (!cpucfg_membase) | |
67 | pr_err("Couldn't map A31 CPU config registers\n"); | |
68 | ||
69 | } | |
70 | ||
71 | static int sun6i_smp_boot_secondary(unsigned int cpu, | |
72 | struct task_struct *idle) | |
73 | { | |
74 | u32 reg; | |
75 | int i; | |
76 | ||
77 | if (!(prcm_membase && cpucfg_membase)) | |
78 | return -EFAULT; | |
79 | ||
80 | spin_lock(&cpu_lock); | |
81 | ||
82 | /* Set CPU boot address */ | |
1146b600 | 83 | writel(virt_to_phys(secondary_startup), |
73346794 MR |
84 | cpucfg_membase + CPUCFG_PRIVATE0_REG); |
85 | ||
86 | /* Assert the CPU core in reset */ | |
87 | writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu)); | |
88 | ||
89 | /* Assert the L1 cache in reset */ | |
90 | reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG); | |
91 | writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG); | |
92 | ||
93 | /* Disable external debug access */ | |
94 | reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG); | |
95 | writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG); | |
96 | ||
97 | /* Power up the CPU */ | |
98 | for (i = 0; i <= 8; i++) | |
99 | writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu)); | |
100 | mdelay(10); | |
101 | ||
102 | /* Clear CPU power-off gating */ | |
103 | reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG); | |
104 | writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG); | |
105 | mdelay(1); | |
106 | ||
107 | /* Deassert the CPU core reset */ | |
108 | writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu)); | |
109 | ||
110 | /* Enable back the external debug accesses */ | |
111 | reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG); | |
112 | writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG); | |
113 | ||
114 | spin_unlock(&cpu_lock); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | struct smp_operations sun6i_smp_ops __initdata = { | |
120 | .smp_prepare_cpus = sun6i_smp_prepare_cpus, | |
121 | .smp_boot_secondary = sun6i_smp_boot_secondary, | |
122 | }; | |
cd4b2b9b | 123 | CPU_METHOD_OF_DECLARE(sun6i_smp, "allwinner,sun6i-a31", &sun6i_smp_ops); |