blob: b575ca439197ea78429b2d275cd0ca83da5aa845 [file] [log] [blame]
David Hendricks8cbd5692017-12-01 20:49:48 -08001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2018 Facebook, Inc.
5 * Copyright 2003-2017 Cavium Inc. <support@cavium.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
17 */
18
19#include <bootmode.h>
20#include <console/console.h>
21#include <cpu/cpu.h>
22#include <device/device.h>
23#include <soc/addressmap.h>
24#include <soc/clock.h>
25#include <soc/sdram.h>
26#include <soc/timer.h>
27#include <stddef.h>
28#include <stdlib.h>
29#include <string.h>
30#include <symbols.h>
31#include <libbdk-boot/bdk-boot.h>
Patrick Rudolphd0c67972018-04-17 13:47:55 +020032#include <soc/ecam0.h>
Patrick Rudolphde4410c2018-03-27 12:01:40 +020033#include <console/uart.h>
34#include <libbdk-hal/bdk-pcie.h>
35#include <soc/ecam0.h>
36#include <device/pci.h>
37#include <libbdk-hal/bdk-qlm.h>
38#include <libbdk-hal/bdk-config.h>
39#include <libbdk-arch/bdk-csrs-bgx.h>
Patrick Rudolph5cdaa332018-04-20 14:43:21 +020040#include <bootmem.h>
41#include <soc/bl31_plat_params.h>
42#include <cbfs.h>
43#include <cbmem.h>
Patrick Rudolphde4410c2018-03-27 12:01:40 +020044#include <fit.h>
45
46static const char *QLM_BGX_MODE_MAP[BDK_QLM_MODE_LAST] = {
47 [BDK_QLM_MODE_SGMII_4X1] = "sgmii",
48 [BDK_QLM_MODE_SGMII_2X1] = "sgmii",
49 [BDK_QLM_MODE_SGMII_1X1] = "sgmii",
50 [BDK_QLM_MODE_XAUI_1X4] = "xaui",
51 [BDK_QLM_MODE_RXAUI_2X2] = "rxaui",
52 [BDK_QLM_MODE_RXAUI_1X2] = "rxaui",
53 [BDK_QLM_MODE_XFI_4X1] = "xfi",
54 [BDK_QLM_MODE_XFI_2X1] = "xfi",
55 [BDK_QLM_MODE_XFI_1X1] = "xfi",
56 [BDK_QLM_MODE_XLAUI_1X4] = "xlaui",
57 [BDK_QLM_MODE_10G_KR_4X1] = "xfi-10g-kr",
58 [BDK_QLM_MODE_10G_KR_2X1] = "xfi-10g-kr",
59 [BDK_QLM_MODE_10G_KR_1X1] = "xfi-10g-kr",
60 [BDK_QLM_MODE_40G_KR4_1X4] = "xlaui-40g-kr",
61 [BDK_QLM_MODE_QSGMII_4X1] = "qsgmii",
62};
63
64static void dt_platform_fixup_phy(struct device_tree_node *node, char *path,
65 int64_t phy_address, bdk_qlm_modes_t qlm_mode)
66{
67 char *data = NULL;
68 size_t size = 0;
69 dt_find_bin_prop(node, "qlm-mode", (void **)&data, &size);
70
71 if (!data || strncmp(data, path, 6) != 0)
72 return; /* No key prefix match. */
73 printk(BIOS_INFO, "%s: Node %s = %s\n", __func__, node->name, data);
74
75 if (strlen(path) == strlen(data) && strcmp(data, path) == 0) {
76 /* Keep node, remove "qlm-mode" property */
77 dt_delete_prop(node, "qlm-mode");
78 printk(BIOS_INFO, "%s: Removing qlm-mode on "
79 "node %s\n", __func__, node->name);
80 /* Linux only access the Phy via MDIO.
81 Remove 'phy-handle' if this option is not available */
82 switch (qlm_mode) {
83 case BDK_QLM_MODE_SGMII_4X1:
84 case BDK_QLM_MODE_SGMII_2X1:
85 case BDK_QLM_MODE_SGMII_1X1:
86 case BDK_QLM_MODE_QSGMII_4X1:
87 if ((phy_address & BDK_IF_PHY_TYPE_MASK) !=
88 BDK_IF_PHY_MDIO) {
89 dt_delete_prop(node, "phy-handle");
90 printk(BIOS_INFO, "%s: Removing phy-handle on "
91 "node %s\n", __func__, node->name);
92 }
93 break;
94 default:
95 break;
96 }
97 } else {
98 printk(BIOS_INFO, "%s: Removing node %s\n", __func__,
99 node->name);
100 /* No match, remove node */
101 list_remove(&node->list_node);
102 }
103}
104
105static void dt_iterate_phy(struct device_tree_node *parent,
106 const char *name,
107 char *path,
108 int64_t phy_address,
109 bdk_qlm_modes_t qlm_mode)
110{
111 struct device_tree_property *prop;
112
113 /* Check if parent itself has the required property value. */
114 list_for_each(prop, parent->properties, list_node) {
115 if (!strcmp(name, prop->prop.name)) {
116 dt_platform_fixup_phy(parent, path, phy_address,
117 qlm_mode);
118 }
119 }
120
121 struct device_tree_node *child;
122 list_for_each(child, parent->children, list_node) {
123 dt_iterate_phy(child, name, path, phy_address, qlm_mode);
124 }
125}
126
127static void dt_platform_fixup_mac(struct device_tree_node *node)
128{
129 const char *name = "local-mac-address";
130 u64 *localmac = NULL;
131 size_t size = 0;
132
133 dt_find_bin_prop(node, name, (void **)&localmac, &size);
134
135 if (!localmac)
136 return;
137 static size_t used_mac;
138
139 /* Extract our MAC address info so we can assign them */
140 size_t next_free_mac_address =
141 bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS);
142 size_t num_free_mac_addresses =
143 bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS_NUM);
144 size_t num_free_override =
145 bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS_NUM_OVERRIDE);
146 if (num_free_override != -1)
147 num_free_mac_addresses = num_free_override;
148
149 if (size == 6) {
150 if (*localmac)
151 return;
152 if (used_mac < num_free_mac_addresses) {
153 *localmac = next_free_mac_address + used_mac;
154 dt_add_bin_prop(node, name, (void *)&localmac, 6);
155 used_mac++;
156 return;
157 }
158 }
159
160 printk(BIOS_INFO, "%s: Removing node %s\n", __func__, node->name);
161 list_remove(&node->list_node);
162}
163
164static void dt_iterate_mac(struct device_tree_node *parent)
165{
166 struct device_tree_property *prop;
167 const char *name = "local-mac-address";
168
169 /* Check if parent itself has the required property value. */
170 list_for_each(prop, parent->properties, list_node) {
171 if (!strcmp(name, prop->prop.name))
172 dt_platform_fixup_mac(parent);
173 }
174
175 struct device_tree_node *child;
176 list_for_each(child, parent->children, list_node) {
177 dt_iterate_mac(child);
178 }
179}
180
181/* Do additional device_tree modifications. */
182static int dt_platform_fixup(struct device_tree_fixup *fixup,
183 struct device_tree *tree)
184{
185 struct device_tree_node *dt_node;
186 size_t i;
187
188 /* Set the sclk clock rate. */
189 dt_node = dt_find_node_by_path(tree->root, "soc@0/sclk", NULL, NULL, 0);
190 if (dt_node) {
191 const u32 freq = thunderx_get_io_clock();
192 printk(BIOS_INFO, "%s: Set SCLK to %u Hz\n", __func__, freq);
193 dt_add_u32_prop(dt_node, "clock-frequency", freq);
194 } else
195 printk(BIOS_ERR, "%s: Node not found. OS might miss-behave !\n",
196 __func__);
197
198 /* Set refclkuaa clock rate. */
199 dt_node = dt_find_node_by_path(tree->root, "soc@0/refclkuaa", NULL,
200 NULL, 0);
201 if (dt_node) {
202 const u32 freq = uart_platform_refclk();
203 printk(BIOS_INFO, "%s: Set REFCLKUAA to %u Hz\n", __func__,
204 freq);
205 dt_add_u32_prop(dt_node, "clock-frequency", freq);
206 } else
207 printk(BIOS_ERR, "%s: Node not found. OS might miss-behave !\n",
208 __func__);
209
210 /* Remove unused PEM entries */
211 for (i = 0; i < 8; i++) {
212 char path[32];
213 u32 phandle = 0;
214 const uint64_t addr = PEM_PEMX_PF_BAR0(i);
215 /* Remove the node */
216 snprintf(path, sizeof(path), "soc@0/pci@%llx", addr);
217 dt_node = dt_find_node_by_path(tree->root, path, NULL, NULL, 0);
218 if (!dt_node || bdk_pcie_is_running(0, i)) {
219 printk(BIOS_INFO, "%s: ignoring %s\n", __func__, path);
220 continue;
221 }
222 /* Store the phandle */
223 phandle = dt_get_phandle(dt_node);
224 printk(BIOS_INFO, "%s: Removing node %s\n", __func__, path);
225 list_remove(&dt_node->list_node);
226
227 /* Remove phandle to non existing nodes */
228 snprintf(path, sizeof(path), "soc@0/smmu0@%llx", SMMU_PF_BAR0);
229 dt_node = dt_find_node_by_path(tree->root, path, NULL, NULL, 0);
230 if (!dt_node) {
231 printk(BIOS_ERR, "%s: SMMU entry not found\n",
232 __func__);
233 continue;
234 }
235 u32 *data = NULL;
236 size_t size = 0;
237 dt_find_bin_prop(dt_node, "mmu-masters", (void **)&data, &size);
238 if (!size) {
239 printk(BIOS_ERR, "%s: mmu-masters entry not found\n",
240 __func__);
241 continue;
242 }
243 for (size_t j = 0; j < size / (sizeof(u32) * 2); j++)
244 if (be32_to_cpu(data[j * 2]) == phandle) {
245 data[j * 2] = 0;
246 data[j * 2 + 1] = 0;
247 break;
248 }
249 }
250
251 /* Remove QLM mode entries */
252 size_t bgx_index, bgx_iface;
253 for (bgx_iface = 0; bgx_iface < 4; bgx_iface++) {
254 for (bgx_index = 0; bgx_index < 4; bgx_index++) {
255 char path[32];
256 int qlm = bdk_qlm_get_qlm_num(0, BDK_IF_BGX,
257 bgx_iface, bgx_index);
258 bdk_qlm_modes_t qlm_mode = (qlm == -1) ?
259 BDK_QLM_MODE_DISABLED :
260 bdk_qlm_get_mode(0, qlm);
261
262 /* BGXX_CMRX_RX_DMAC_CTL is used to mark ports as
263 * disabled that would otherwise be enabled */
264 if (qlm_mode != BDK_QLM_MODE_DISABLED) {
265 BDK_CSR_INIT(rx_dmac_ctl, 0,
266 BDK_BGXX_CMRX_RX_DMAC_CTL(bgx_iface,
267 bgx_index));
268 if (rx_dmac_ctl.u == 0)
269 qlm_mode = BDK_QLM_MODE_DISABLED;
270 }
271
272 if (qlm_mode == BDK_QLM_MODE_DISABLED)
273 snprintf(path, sizeof(path), "0x0%x%x,disabled",
274 bgx_iface, bgx_index);
275 else
276 snprintf(path, sizeof(path), "0x0%x%x,%s",
277 bgx_iface, bgx_index,
278 QLM_BGX_MODE_MAP[qlm_mode]);
279
280 int64_t phy_address =
281 bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, 0,
282 bgx_iface, bgx_index);
283
284 dt_iterate_phy(tree->root, "qlm-mode", path,
285 phy_address, qlm_mode);
286 }
287 }
288
289 /* Set local MAC address */
290 dt_iterate_mac(tree->root);
291
292 return 0;
293}
David Hendricks8cbd5692017-12-01 20:49:48 -0800294
Patrick Rudolph5cdaa332018-04-20 14:43:21 +0200295extern u8 _bl31[];
296extern u8 _ebl31[];
297extern u8 _sff8104[];
298extern u8 _esff8104[];
299
300void bootmem_platform_add_ranges(void)
301{
302 /* ATF reserved */
303 bootmem_add_range((uintptr_t)_bl31,
304 ((uintptr_t)_ebl31 - (uintptr_t)_bl31),
305 BM_MEM_RESERVED);
306
307 bootmem_add_range((uintptr_t)_sff8104,
308 ((uintptr_t)_esff8104 - (uintptr_t)_sff8104),
309 BM_MEM_RESERVED);
310
311 /* Scratchpad for ATF SATA quirks */
312 bootmem_add_range(sdram_size_mb() * KiB, 1 * MiB, BM_MEM_RESERVED);
313}
314
David Hendricks8cbd5692017-12-01 20:49:48 -0800315static void soc_read_resources(device_t dev)
316{
Patrick Rudolpheead8792018-08-10 13:53:04 +0200317 // HACK: Don't advertise bootblock romstage CAR region, it's broken...
318 ram_resource(dev, 0, 2 * KiB, sdram_size_mb() * KiB - 2 * KiB);
David Hendricks8cbd5692017-12-01 20:49:48 -0800319}
320
Patrick Rudolph5cdaa332018-04-20 14:43:21 +0200321static void soc_init_atf(void)
322{
323 static struct bl31_fdt_param fdt_param = {
324 .h = { .type = PARAM_FDT, },
325 };
326
327 size_t size = 0;
328
329 void *ptr = cbfs_boot_map_with_leak("sff8104-linux.dtb",
330 CBFS_TYPE_RAW, &size);
331 if (ptr)
332 memcpy(_sff8104, ptr, size);
333 /* Point to devicetree in secure memory */
334 fdt_param.fdt_ptr = (uintptr_t)_sff8104;
335
336 register_bl31_param(&fdt_param.h);
337
338 static struct bl31_u64_param cbtable_param = {
339 .h = { .type = PARAM_COREBOOT_TABLE, },
340 };
341 /* Point to coreboot tables */
342 cbtable_param.value = (uint64_t)cbmem_find(CBMEM_ID_CBTABLE);
343 if (cbtable_param.value)
344 register_bl31_param(&cbtable_param.h);
345}
346
David Hendricks8cbd5692017-12-01 20:49:48 -0800347static void soc_init(device_t dev)
348{
349 /* Init ECAM, MDIO, PEM, PHY, QLM ... */
350 bdk_boot();
351
Patrick Rudolphde4410c2018-03-27 12:01:40 +0200352 if (IS_ENABLED(CONFIG_PAYLOAD_FIT_SUPPORT)) {
353 struct device_tree_fixup *dt_fixup;
354
355 dt_fixup = malloc(sizeof(*dt_fixup));
356 if (dt_fixup) {
357 dt_fixup->fixup = dt_platform_fixup;
358 list_insert_after(&dt_fixup->list_node,
359 &device_tree_fixups);
360 }
361 }
Patrick Rudolph5cdaa332018-04-20 14:43:21 +0200362
363 if (IS_ENABLED(CONFIG_ARM64_USE_ARM_TRUSTED_FIRMWARE))
364 soc_init_atf();
David Hendricks8cbd5692017-12-01 20:49:48 -0800365}
366
367static void soc_final(device_t dev)
368{
369 watchdog_disable(0);
370}
371
372static struct device_operations soc_ops = {
Patrick Rudolph88f81af2018-04-11 11:40:55 +0200373 .read_resources = soc_read_resources,
374 .set_resources = DEVICE_NOOP,
375 .enable_resources = DEVICE_NOOP,
376 .init = soc_init,
377 .final = soc_final,
378 .scan_bus = NULL,
David Hendricks8cbd5692017-12-01 20:49:48 -0800379};
380
381static void enable_soc_dev(device_t dev)
382{
Patrick Rudolphd0c67972018-04-17 13:47:55 +0200383 if (dev->path.type == DEVICE_PATH_DOMAIN &&
384 dev->path.domain.domain == 0) {
385 dev->ops = &pci_domain_ops_ecam0;
386 } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) {
387 dev->ops = &soc_ops;
388 }
David Hendricks8cbd5692017-12-01 20:49:48 -0800389}
390
391struct chip_operations soc_cavium_cn81xx_ops = {
392 CHIP_NAME("SOC Cavium CN81XX")
393 .enable_dev = enable_soc_dev,
394};