blob: 370e2e83a99d30a09083912069accf401ff7f4ff [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>
40
41#include <fit.h>
42
43static const char *QLM_BGX_MODE_MAP[BDK_QLM_MODE_LAST] = {
44 [BDK_QLM_MODE_SGMII_4X1] = "sgmii",
45 [BDK_QLM_MODE_SGMII_2X1] = "sgmii",
46 [BDK_QLM_MODE_SGMII_1X1] = "sgmii",
47 [BDK_QLM_MODE_XAUI_1X4] = "xaui",
48 [BDK_QLM_MODE_RXAUI_2X2] = "rxaui",
49 [BDK_QLM_MODE_RXAUI_1X2] = "rxaui",
50 [BDK_QLM_MODE_XFI_4X1] = "xfi",
51 [BDK_QLM_MODE_XFI_2X1] = "xfi",
52 [BDK_QLM_MODE_XFI_1X1] = "xfi",
53 [BDK_QLM_MODE_XLAUI_1X4] = "xlaui",
54 [BDK_QLM_MODE_10G_KR_4X1] = "xfi-10g-kr",
55 [BDK_QLM_MODE_10G_KR_2X1] = "xfi-10g-kr",
56 [BDK_QLM_MODE_10G_KR_1X1] = "xfi-10g-kr",
57 [BDK_QLM_MODE_40G_KR4_1X4] = "xlaui-40g-kr",
58 [BDK_QLM_MODE_QSGMII_4X1] = "qsgmii",
59};
60
61static void dt_platform_fixup_phy(struct device_tree_node *node, char *path,
62 int64_t phy_address, bdk_qlm_modes_t qlm_mode)
63{
64 char *data = NULL;
65 size_t size = 0;
66 dt_find_bin_prop(node, "qlm-mode", (void **)&data, &size);
67
68 if (!data || strncmp(data, path, 6) != 0)
69 return; /* No key prefix match. */
70 printk(BIOS_INFO, "%s: Node %s = %s\n", __func__, node->name, data);
71
72 if (strlen(path) == strlen(data) && strcmp(data, path) == 0) {
73 /* Keep node, remove "qlm-mode" property */
74 dt_delete_prop(node, "qlm-mode");
75 printk(BIOS_INFO, "%s: Removing qlm-mode on "
76 "node %s\n", __func__, node->name);
77 /* Linux only access the Phy via MDIO.
78 Remove 'phy-handle' if this option is not available */
79 switch (qlm_mode) {
80 case BDK_QLM_MODE_SGMII_4X1:
81 case BDK_QLM_MODE_SGMII_2X1:
82 case BDK_QLM_MODE_SGMII_1X1:
83 case BDK_QLM_MODE_QSGMII_4X1:
84 if ((phy_address & BDK_IF_PHY_TYPE_MASK) !=
85 BDK_IF_PHY_MDIO) {
86 dt_delete_prop(node, "phy-handle");
87 printk(BIOS_INFO, "%s: Removing phy-handle on "
88 "node %s\n", __func__, node->name);
89 }
90 break;
91 default:
92 break;
93 }
94 } else {
95 printk(BIOS_INFO, "%s: Removing node %s\n", __func__,
96 node->name);
97 /* No match, remove node */
98 list_remove(&node->list_node);
99 }
100}
101
102static void dt_iterate_phy(struct device_tree_node *parent,
103 const char *name,
104 char *path,
105 int64_t phy_address,
106 bdk_qlm_modes_t qlm_mode)
107{
108 struct device_tree_property *prop;
109
110 /* Check if parent itself has the required property value. */
111 list_for_each(prop, parent->properties, list_node) {
112 if (!strcmp(name, prop->prop.name)) {
113 dt_platform_fixup_phy(parent, path, phy_address,
114 qlm_mode);
115 }
116 }
117
118 struct device_tree_node *child;
119 list_for_each(child, parent->children, list_node) {
120 dt_iterate_phy(child, name, path, phy_address, qlm_mode);
121 }
122}
123
124static void dt_platform_fixup_mac(struct device_tree_node *node)
125{
126 const char *name = "local-mac-address";
127 u64 *localmac = NULL;
128 size_t size = 0;
129
130 dt_find_bin_prop(node, name, (void **)&localmac, &size);
131
132 if (!localmac)
133 return;
134 static size_t used_mac;
135
136 /* Extract our MAC address info so we can assign them */
137 size_t next_free_mac_address =
138 bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS);
139 size_t num_free_mac_addresses =
140 bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS_NUM);
141 size_t num_free_override =
142 bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS_NUM_OVERRIDE);
143 if (num_free_override != -1)
144 num_free_mac_addresses = num_free_override;
145
146 if (size == 6) {
147 if (*localmac)
148 return;
149 if (used_mac < num_free_mac_addresses) {
150 *localmac = next_free_mac_address + used_mac;
151 dt_add_bin_prop(node, name, (void *)&localmac, 6);
152 used_mac++;
153 return;
154 }
155 }
156
157 printk(BIOS_INFO, "%s: Removing node %s\n", __func__, node->name);
158 list_remove(&node->list_node);
159}
160
161static void dt_iterate_mac(struct device_tree_node *parent)
162{
163 struct device_tree_property *prop;
164 const char *name = "local-mac-address";
165
166 /* Check if parent itself has the required property value. */
167 list_for_each(prop, parent->properties, list_node) {
168 if (!strcmp(name, prop->prop.name))
169 dt_platform_fixup_mac(parent);
170 }
171
172 struct device_tree_node *child;
173 list_for_each(child, parent->children, list_node) {
174 dt_iterate_mac(child);
175 }
176}
177
178/* Do additional device_tree modifications. */
179static int dt_platform_fixup(struct device_tree_fixup *fixup,
180 struct device_tree *tree)
181{
182 struct device_tree_node *dt_node;
183 size_t i;
184
185 /* Set the sclk clock rate. */
186 dt_node = dt_find_node_by_path(tree->root, "soc@0/sclk", NULL, NULL, 0);
187 if (dt_node) {
188 const u32 freq = thunderx_get_io_clock();
189 printk(BIOS_INFO, "%s: Set SCLK to %u Hz\n", __func__, freq);
190 dt_add_u32_prop(dt_node, "clock-frequency", freq);
191 } else
192 printk(BIOS_ERR, "%s: Node not found. OS might miss-behave !\n",
193 __func__);
194
195 /* Set refclkuaa clock rate. */
196 dt_node = dt_find_node_by_path(tree->root, "soc@0/refclkuaa", NULL,
197 NULL, 0);
198 if (dt_node) {
199 const u32 freq = uart_platform_refclk();
200 printk(BIOS_INFO, "%s: Set REFCLKUAA to %u Hz\n", __func__,
201 freq);
202 dt_add_u32_prop(dt_node, "clock-frequency", freq);
203 } else
204 printk(BIOS_ERR, "%s: Node not found. OS might miss-behave !\n",
205 __func__);
206
207 /* Remove unused PEM entries */
208 for (i = 0; i < 8; i++) {
209 char path[32];
210 u32 phandle = 0;
211 const uint64_t addr = PEM_PEMX_PF_BAR0(i);
212 /* Remove the node */
213 snprintf(path, sizeof(path), "soc@0/pci@%llx", addr);
214 dt_node = dt_find_node_by_path(tree->root, path, NULL, NULL, 0);
215 if (!dt_node || bdk_pcie_is_running(0, i)) {
216 printk(BIOS_INFO, "%s: ignoring %s\n", __func__, path);
217 continue;
218 }
219 /* Store the phandle */
220 phandle = dt_get_phandle(dt_node);
221 printk(BIOS_INFO, "%s: Removing node %s\n", __func__, path);
222 list_remove(&dt_node->list_node);
223
224 /* Remove phandle to non existing nodes */
225 snprintf(path, sizeof(path), "soc@0/smmu0@%llx", SMMU_PF_BAR0);
226 dt_node = dt_find_node_by_path(tree->root, path, NULL, NULL, 0);
227 if (!dt_node) {
228 printk(BIOS_ERR, "%s: SMMU entry not found\n",
229 __func__);
230 continue;
231 }
232 u32 *data = NULL;
233 size_t size = 0;
234 dt_find_bin_prop(dt_node, "mmu-masters", (void **)&data, &size);
235 if (!size) {
236 printk(BIOS_ERR, "%s: mmu-masters entry not found\n",
237 __func__);
238 continue;
239 }
240 for (size_t j = 0; j < size / (sizeof(u32) * 2); j++)
241 if (be32_to_cpu(data[j * 2]) == phandle) {
242 data[j * 2] = 0;
243 data[j * 2 + 1] = 0;
244 break;
245 }
246 }
247
248 /* Remove QLM mode entries */
249 size_t bgx_index, bgx_iface;
250 for (bgx_iface = 0; bgx_iface < 4; bgx_iface++) {
251 for (bgx_index = 0; bgx_index < 4; bgx_index++) {
252 char path[32];
253 int qlm = bdk_qlm_get_qlm_num(0, BDK_IF_BGX,
254 bgx_iface, bgx_index);
255 bdk_qlm_modes_t qlm_mode = (qlm == -1) ?
256 BDK_QLM_MODE_DISABLED :
257 bdk_qlm_get_mode(0, qlm);
258
259 /* BGXX_CMRX_RX_DMAC_CTL is used to mark ports as
260 * disabled that would otherwise be enabled */
261 if (qlm_mode != BDK_QLM_MODE_DISABLED) {
262 BDK_CSR_INIT(rx_dmac_ctl, 0,
263 BDK_BGXX_CMRX_RX_DMAC_CTL(bgx_iface,
264 bgx_index));
265 if (rx_dmac_ctl.u == 0)
266 qlm_mode = BDK_QLM_MODE_DISABLED;
267 }
268
269 if (qlm_mode == BDK_QLM_MODE_DISABLED)
270 snprintf(path, sizeof(path), "0x0%x%x,disabled",
271 bgx_iface, bgx_index);
272 else
273 snprintf(path, sizeof(path), "0x0%x%x,%s",
274 bgx_iface, bgx_index,
275 QLM_BGX_MODE_MAP[qlm_mode]);
276
277 int64_t phy_address =
278 bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, 0,
279 bgx_iface, bgx_index);
280
281 dt_iterate_phy(tree->root, "qlm-mode", path,
282 phy_address, qlm_mode);
283 }
284 }
285
286 /* Set local MAC address */
287 dt_iterate_mac(tree->root);
288
289 return 0;
290}
David Hendricks8cbd5692017-12-01 20:49:48 -0800291
292static void soc_read_resources(device_t dev)
293{
294 ram_resource(dev, 0, (uintptr_t)_dram / KiB, sdram_size_mb() * KiB);
295}
296
297static void soc_init(device_t dev)
298{
299 /* Init ECAM, MDIO, PEM, PHY, QLM ... */
300 bdk_boot();
301
302 /* TODO: additional trustzone init */
Patrick Rudolphde4410c2018-03-27 12:01:40 +0200303
304 if (IS_ENABLED(CONFIG_PAYLOAD_FIT_SUPPORT)) {
305 struct device_tree_fixup *dt_fixup;
306
307 dt_fixup = malloc(sizeof(*dt_fixup));
308 if (dt_fixup) {
309 dt_fixup->fixup = dt_platform_fixup;
310 list_insert_after(&dt_fixup->list_node,
311 &device_tree_fixups);
312 }
313 }
David Hendricks8cbd5692017-12-01 20:49:48 -0800314}
315
316static void soc_final(device_t dev)
317{
318 watchdog_disable(0);
319}
320
321static struct device_operations soc_ops = {
Patrick Rudolph88f81af2018-04-11 11:40:55 +0200322 .read_resources = soc_read_resources,
323 .set_resources = DEVICE_NOOP,
324 .enable_resources = DEVICE_NOOP,
325 .init = soc_init,
326 .final = soc_final,
327 .scan_bus = NULL,
David Hendricks8cbd5692017-12-01 20:49:48 -0800328};
329
330static void enable_soc_dev(device_t dev)
331{
Patrick Rudolphd0c67972018-04-17 13:47:55 +0200332 if (dev->path.type == DEVICE_PATH_DOMAIN &&
333 dev->path.domain.domain == 0) {
334 dev->ops = &pci_domain_ops_ecam0;
335 } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) {
336 dev->ops = &soc_ops;
337 }
David Hendricks8cbd5692017-12-01 20:49:48 -0800338}
339
340struct chip_operations soc_cavium_cn81xx_ops = {
341 CHIP_NAME("SOC Cavium CN81XX")
342 .enable_dev = enable_soc_dev,
343};