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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2024 9elements GmbH
*/
#include <cpu.h>
#include <cpu_func.h>
#include <dm.h>
#include <fdt_support.h>
#include <acpi/acpigen.h>
#include <asm/armv8/cpu.h>
#include <asm/cache.h>
#include <asm/io.h>
#include <asm/global_data.h>
#include <asm/system.h>
#include <asm-generic/sections.h>
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include "armv8_cpu.h"
DECLARE_GLOBAL_DATA_PTR;
struct bcm_plat {
u64 release_addr;
};
static int cpu_bcm_get_desc(const struct udevice *dev, char *buf, int size)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
const char *name;
if (size < 32)
return -ENOSPC;
if (device_is_compatible(dev, "arm,cortex-a53"))
name = "A53";
else if (device_is_compatible(dev, "arm,cortex-a72"))
name = "A72";
else
name = "?";
snprintf(buf, size, "Broadcom Cortex-%s at %u MHz\n",
name, plat->timebase_freq);
return 0;
}
static int cpu_bcm_get_info(const struct udevice *dev, struct cpu_info *info)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
info->cpu_freq = plat->timebase_freq * 1000;
info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
return 0;
}
static int cpu_bcm_get_count(const struct udevice *dev)
{
return uclass_id_count(UCLASS_CPU);
}
static int cpu_bcm_get_vendor(const struct udevice *dev, char *buf, int size)
{
snprintf(buf, size, "Broadcom");
return 0;
}
static int cpu_bcm_is_current(struct udevice *dev)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
if (plat->cpu_id == (read_mpidr() & 0xffff))
return 1;
return 0;
}
/**
* bcm_cpu_on - Releases the secondary CPU from it's spintable
*
* Write the CPU's spintable mailbox and let the CPU enter U-Boot.
*
* @dev: Device to start
* @return: zero on success or error code on failure.
*/
static int bcm_cpu_on(struct udevice *dev)
{
struct bcm_plat *plat = dev_get_plat(dev);
ulong *start_address;
if (plat->release_addr == ~0ULL)
return -ENODATA;
start_address = map_physmem(plat->release_addr, sizeof(uintptr_t), MAP_NOCACHE);
/* Point secondary CPU to U-Boot entry */
*start_address = (uintptr_t)_start;
/* Make sure the other CPUs see the written start address */
if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF))
flush_dcache_all();
/* Send an event to wake up the secondary CPU. */
asm("dsb ishst\n"
"sev");
unmap_physmem(start_address, MAP_NOCACHE);
return 0;
}
static const struct cpu_ops cpu_bcm_ops = {
.get_desc = cpu_bcm_get_desc,
.get_info = cpu_bcm_get_info,
.get_count = cpu_bcm_get_count,
.get_vendor = cpu_bcm_get_vendor,
.is_current = cpu_bcm_is_current,
};
static const struct udevice_id cpu_bcm_ids[] = {
{ .compatible = "arm,cortex-a53" }, /* RPi 3 */
{ .compatible = "arm,cortex-a72" }, /* RPi 4 */
{ }
};
static int bcm_cpu_bind(struct udevice *dev)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
plat->cpu_id = dev_read_addr(dev);
return 0;
}
/**
* bcm_cpu_of_to_plat - Gather spin-table release address
*
* Read the spin-table release address to allow all seconary CPUs to enter
* U-Boot when necessary.
*
* @dev: Device to start
*/
static int bcm_cpu_of_to_plat(struct udevice *dev)
{
struct bcm_plat *plat = dev_get_plat(dev);
const char *prop;
if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) {
plat->release_addr = ~0ULL;
prop = dev_read_string(dev, "enable-method");
if (!prop || strcmp(prop, "spin-table"))
return -ENODEV;
plat->release_addr = dev_read_u64_default(dev, "cpu-release-addr", ~0ULL);
if (plat->release_addr == ~0ULL)
return -ENODEV;
}
return 0;
}
static int bcm_cpu_probe(struct udevice *dev)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
struct clk clk;
int ret;
/* Get a clock if it exists */
ret = clk_get_by_index(dev, 0, &clk);
if (!ret) {
ret = clk_enable(&clk);
if (ret && (ret != -ENOSYS || ret != -EOPNOTSUPP))
return ret;
ret = clk_get_rate(&clk);
if (IS_ERR_VALUE(ret))
return ret;
plat->timebase_freq = ret;
}
/*
* The armstub holds the secondary CPUs in a spinloop. When
* ARMV8_MULTIENTRY is enabled release the secondary CPUs and
* let them enter U-Boot as well.
*/
if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) {
ret = bcm_cpu_on(dev);
if (ret)
return ret;
}
return ret;
}
struct acpi_ops bcm283x_cpu_acpi_ops = {
.fill_ssdt = armv8_cpu_fill_ssdt,
.fill_madt = armv8_cpu_fill_madt,
};
U_BOOT_DRIVER(cpu_bcm_drv) = {
.name = "bcm283x_cpu",
.id = UCLASS_CPU,
.of_match = cpu_bcm_ids,
.ops = &cpu_bcm_ops,
.probe = bcm_cpu_probe,
.bind = bcm_cpu_bind,
.of_to_plat = bcm_cpu_of_to_plat,
.plat_auto = sizeof(struct bcm_plat),
ACPI_OPS_PTR(&bcm283x_cpu_acpi_ops)
};
|