1// SPDX-License-Identifier:    GPL-2.0
2/*
3 * Copyright (C) 2018 Marvell International Ltd.
4 *
5 * https://spdx.org/licenses
6 */
7
8#include <errno.h>
9#include <env.h>
10#include <log.h>
11#include <net.h>
12#include <asm/io.h>
13#include <linux/compiler.h>
14#include <linux/libfdt.h>
15#include <fdtdec.h>
16#include <fdt_support.h>
17#include <asm/arch/board.h>
18#include <asm/global_data.h>
19
20DECLARE_GLOBAL_DATA_PTR;
21
22static int fdt_get_mdio_bus(const void *fdt, int phy_offset)
23{
24	int node, bus = -1;
25	const u64 *reg;
26	u64 addr;
27
28	if (phy_offset < 0)
29		return -1;
30	/* obtain mdio node and get the reg prop */
31	node = fdt_parent_offset(fdt, phy_offset);
32	if (node < 0)
33		return -1;
34
35	reg = fdt_getprop(fdt, node, "reg", NULL);
36	addr = fdt64_to_cpu(*reg);
37	bus = (addr & (1 << 7)) ? 1 : 0;
38	return bus;
39}
40
41static int fdt_get_phy_addr(const void *fdt, int phy_offset)
42{
43	const u32 *reg;
44	int addr = -1;
45
46	if (phy_offset < 0)
47		return -1;
48	reg = fdt_getprop(fdt, phy_offset, "reg", NULL);
49	addr = fdt32_to_cpu(*reg);
50	return addr;
51}
52
53void fdt_parse_phy_info(void)
54{
55	const void *fdt = gd->fdt_blob;
56	int offset = 0, node, bgx_id = 0, lmacid = 0;
57	const u32 *val;
58	char bgxname[24];
59	int len, rgx_id = 0, eth_id = 0;
60	int phandle, phy_offset;
61	int subnode, i;
62	int bdknode;
63
64	bdknode = fdt_path_offset(fdt, "/cavium,bdk");
65	if (bdknode < 0) {
66		printf("%s: bdk node is missing from device tree: %s\n",
67		       __func__, fdt_strerror(bdknode));
68	}
69
70	offset = fdt_node_offset_by_compatible(fdt, -1, "pci-bridge");
71	if (offset < 1)
72		return;
73
74	for (bgx_id = 0; bgx_id < MAX_BGX_PER_NODE; bgx_id++) {
75		int phy_addr[LMAC_CNT] = {[0 ... LMAC_CNT - 1] = -1};
76		bool autoneg_dis[LMAC_CNT] = {[0 ... LMAC_CNT - 1] = 0};
77		int mdio_bus[LMAC_CNT] = {[0 ... LMAC_CNT - 1] = -1};
78		bool lmac_reg[LMAC_CNT] = {[0 ... LMAC_CNT - 1] = 0};
79		bool lmac_enable[LMAC_CNT] = {[0 ... LMAC_CNT - 1] = 0};
80
81		snprintf(bgxname, sizeof(bgxname), "bgx%d", bgx_id);
82		node = fdt_subnode_offset(fdt, offset, bgxname);
83		if (node < 0) {
84			/* check if it is rgx node */
85			snprintf(bgxname, sizeof(bgxname), "rgx%d", rgx_id);
86			node = fdt_subnode_offset(fdt, offset, bgxname);
87			if (node < 0) {
88				debug("bgx%d/rgx0 node not found\n", bgx_id);
89				return;
90			}
91		}
92		debug("bgx%d node found\n", bgx_id);
93
94		/*
95		 * loop through each of the bgx/rgx nodes
96		 * to find PHY nodes
97		 */
98		fdt_for_each_subnode(subnode, fdt, node) {
99			/* Check for reg property */
100			val = fdt_getprop(fdt, subnode, "reg", &len);
101			if (val) {
102				debug("lmacid = %d\n", lmacid);
103				lmac_reg[lmacid] = 1;
104			}
105			/* check for phy-handle property */
106			val = fdt_getprop(fdt, subnode, "phy-handle", &len);
107			if (val) {
108				phandle = fdt32_to_cpu(*val);
109				if (!phandle) {
110					debug("phandle not valid %d\n", lmacid);
111				} else {
112					phy_offset = fdt_node_offset_by_phandle
113							(fdt, phandle);
114					phy_addr[lmacid] = fdt_get_phy_addr
115							(fdt, phy_offset);
116					mdio_bus[lmacid] = fdt_get_mdio_bus
117							(fdt, phy_offset);
118					}
119				} else {
120					debug("phy-handle prop not found %d\n",
121					      lmacid);
122				}
123				/* check for autonegotiation property */
124				val = fdt_getprop(fdt, subnode,
125						  "cavium,disable-autonegotiation",
126						  &len);
127				if (val)
128					autoneg_dis[lmacid] = 1;
129
130				eth_id++;
131				lmacid++;
132			}
133
134			for (i = 0; i < MAX_LMAC_PER_BGX; i++) {
135				const char *str;
136
137				snprintf(bgxname, sizeof(bgxname),
138					 "BGX-ENABLE.N0.BGX%d.P%d", bgx_id, i);
139				if (bdknode >= 0) {
140					str = fdt_getprop(fdt, bdknode,
141							  bgxname, &len);
142					if (str)
143						lmac_enable[i] =
144							simple_strtol(str, NULL,
145								      10);
146				}
147			}
148
149			lmacid = 0;
150			bgx_set_board_info(bgx_id, mdio_bus, phy_addr,
151					   autoneg_dis, lmac_reg, lmac_enable);
152		}
153}
154
155static int fdt_get_bdk_node(void)
156{
157	int node, ret;
158	const void *fdt = gd->fdt_blob;
159
160	if (!fdt) {
161		printf("ERROR: %s: no valid device tree found\n", __func__);
162		return 0;
163	}
164
165	ret = fdt_check_header(fdt);
166	if (ret < 0) {
167		printf("fdt: %s\n", fdt_strerror(ret));
168		return 0;
169	}
170
171	node = fdt_path_offset(fdt, "/cavium,bdk");
172	if (node < 0) {
173		printf("%s: /cavium,bdk is missing from device tree: %s\n",
174		       __func__, fdt_strerror(node));
175		return 0;
176	}
177	return node;
178}
179
180const char *fdt_get_board_serial(void)
181{
182	const void *fdt = gd->fdt_blob;
183	int node, len = 64;
184	const char *str = NULL;
185
186	node = fdt_get_bdk_node();
187	if (!node)
188		return NULL;
189
190	str = fdt_getprop(fdt, node, "BOARD-SERIAL", &len);
191	if (!str)
192		printf("Error: cannot retrieve board serial from fdt\n");
193	return str;
194}
195
196const char *fdt_get_board_revision(void)
197{
198	const void *fdt = gd->fdt_blob;
199	int node, len = 64;
200	const char *str = NULL;
201
202	node = fdt_get_bdk_node();
203	if (!node)
204		return NULL;
205
206	str = fdt_getprop(fdt, node, "BOARD-REVISION", &len);
207	if (!str)
208		printf("Error: cannot retrieve board revision from fdt\n");
209	return str;
210}
211
212const char *fdt_get_board_model(void)
213{
214	const void *fdt = gd->fdt_blob;
215	int node, len = 16;
216	const char *str = NULL;
217
218	node = fdt_get_bdk_node();
219	if (!node)
220		return NULL;
221
222	str = fdt_getprop(fdt, node, "BOARD-MODEL", &len);
223	if (!str)
224		printf("Error: cannot retrieve board model from fdt\n");
225	return str;
226}
227
228void fdt_board_get_ethaddr(int bgx, int lmac, unsigned char *eth)
229{
230	const void *fdt = gd->fdt_blob;
231	const char *mac = NULL;
232	int offset = 0, node, len;
233	int subnode, i = 0;
234	char bgxname[24];
235
236	offset = fdt_node_offset_by_compatible(fdt, -1, "pci-bridge");
237	if (offset < 0) {
238		printf("%s couldn't find mrml bridge node in fdt\n",
239		       __func__);
240		return;
241	}
242	if (bgx == 2 && otx_is_soc(CN81XX)) {
243		snprintf(bgxname, sizeof(bgxname), "rgx%d", 0);
244		lmac = 0;
245	} else {
246		snprintf(bgxname, sizeof(bgxname), "bgx%d", bgx);
247	}
248
249	node = fdt_subnode_offset(fdt, offset, bgxname);
250
251	fdt_for_each_subnode(subnode, fdt, node) {
252		if (i++ != lmac)
253			continue;
254		/* check for local-mac-address */
255		mac = fdt_getprop(fdt, subnode, "local-mac-address", &len);
256		if (mac) {
257			debug("%s mac %pM\n", __func__, mac);
258			memcpy(eth, mac, ARP_HLEN);
259		} else {
260			memset(eth, 0, ARP_HLEN);
261		}
262		debug("%s eth %pM\n", __func__, eth);
263		return;
264	}
265}
266
267int arch_fixup_memory_node(void *blob)
268{
269	return 0;
270}
271
272int ft_board_setup(void *blob, struct bd_info *bd)
273{
274	/* remove "cavium, bdk" node from DT */
275	int ret = 0, offset;
276
277	ret = fdt_check_header(blob);
278	if (ret < 0) {
279		printf("ERROR: %s\n", fdt_strerror(ret));
280		return ret;
281	}
282
283	if (blob) {
284		/* delete cavium,bdk node if it exists */
285		offset = fdt_path_offset(blob, "/cavium,bdk");
286		if (offset >= 0) {
287			ret = fdt_del_node(blob, offset);
288			if (ret < 0) {
289				printf("WARNING : could not remove bdk node\n");
290				return ret;
291			}
292			debug("%s deleted bdk node\n", __func__);
293		}
294	}
295
296	return 0;
297}
298
299/**
300 * Return the FDT base address that was passed by ATF
301 *
302 * Return:	FDT base address received from ATF in x1 register
303 */
304void *board_fdt_blob_setup(int *err)
305{
306	*err = 0;
307	return (void *)fdt_base_addr;
308}
309