xref: /freebsd-src/sys/dev/vnic/thunder_bgx_fdt.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
12306b72aSZbigniew Bodek /*-
22306b72aSZbigniew Bodek  * Copyright (c) 2015 The FreeBSD Foundation
32306b72aSZbigniew Bodek  *
42306b72aSZbigniew Bodek  * This software was developed by Semihalf under
52306b72aSZbigniew Bodek  * the sponsorship of the FreeBSD Foundation.
62306b72aSZbigniew Bodek  *
72306b72aSZbigniew Bodek  * Redistribution and use in source and binary forms, with or without
82306b72aSZbigniew Bodek  * modification, are permitted provided that the following conditions
92306b72aSZbigniew Bodek  * are met:
102306b72aSZbigniew Bodek  * 1. Redistributions of source code must retain the above copyright
112306b72aSZbigniew Bodek  *    notice, this list of conditions and the following disclaimer.
122306b72aSZbigniew Bodek  * 2. Redistributions in binary form must reproduce the above copyright
132306b72aSZbigniew Bodek  *    notice, this list of conditions and the following disclaimer in the
142306b72aSZbigniew Bodek  *    documentation and/or other materials provided with the distribution.
152306b72aSZbigniew Bodek  *
162306b72aSZbigniew Bodek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172306b72aSZbigniew Bodek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182306b72aSZbigniew Bodek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192306b72aSZbigniew Bodek  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202306b72aSZbigniew Bodek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212306b72aSZbigniew Bodek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222306b72aSZbigniew Bodek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232306b72aSZbigniew Bodek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242306b72aSZbigniew Bodek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252306b72aSZbigniew Bodek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262306b72aSZbigniew Bodek  * SUCH DAMAGE.
272306b72aSZbigniew Bodek  */
282306b72aSZbigniew Bodek 
292306b72aSZbigniew Bodek #include <sys/param.h>
302306b72aSZbigniew Bodek #include <sys/systm.h>
312306b72aSZbigniew Bodek #include <sys/bitset.h>
322306b72aSZbigniew Bodek #include <sys/bitstring.h>
332306b72aSZbigniew Bodek #include <sys/bus.h>
34*6074a3c0SMark Johnston #include <sys/ctype.h>
352306b72aSZbigniew Bodek #include <sys/endian.h>
362306b72aSZbigniew Bodek #include <sys/kernel.h>
372306b72aSZbigniew Bodek #include <sys/malloc.h>
382306b72aSZbigniew Bodek #include <sys/module.h>
392306b72aSZbigniew Bodek #include <sys/rman.h>
402306b72aSZbigniew Bodek #include <sys/pciio.h>
412306b72aSZbigniew Bodek #include <sys/pcpu.h>
422306b72aSZbigniew Bodek #include <sys/proc.h>
432306b72aSZbigniew Bodek #include <sys/socket.h>
442306b72aSZbigniew Bodek #include <sys/cpuset.h>
452306b72aSZbigniew Bodek #include <sys/lock.h>
462306b72aSZbigniew Bodek #include <sys/mutex.h>
472306b72aSZbigniew Bodek 
482306b72aSZbigniew Bodek #include <net/ethernet.h>
492306b72aSZbigniew Bodek #include <net/if.h>
502306b72aSZbigniew Bodek #include <net/if_media.h>
512306b72aSZbigniew Bodek 
522306b72aSZbigniew Bodek #include <dev/ofw/openfirm.h>
53be624ad4SZbigniew Bodek #include <dev/ofw/ofw_bus.h>
542306b72aSZbigniew Bodek #include <dev/mii/miivar.h>
552306b72aSZbigniew Bodek 
562306b72aSZbigniew Bodek #include "thunder_bgx.h"
572306b72aSZbigniew Bodek #include "thunder_bgx_var.h"
582306b72aSZbigniew Bodek 
592306b72aSZbigniew Bodek #define	CONN_TYPE_MAXLEN	16
602306b72aSZbigniew Bodek #define	CONN_TYPE_OFFSET	2
612306b72aSZbigniew Bodek 
62be624ad4SZbigniew Bodek #define	BGX_NODE_NAME		"bgx"
63be624ad4SZbigniew Bodek #define	BGX_MAXID		9
6419fbe8bdSZbigniew Bodek /* BGX func. 0, i.e.: reg = <0x8000 0 0 0 0>; DEVFN = 0x80 */
6519fbe8bdSZbigniew Bodek #define	BGX_DEVFN_0		0x80
66be624ad4SZbigniew Bodek 
67be624ad4SZbigniew Bodek #define	FDT_NAME_MAXLEN		31
68be624ad4SZbigniew Bodek 
692306b72aSZbigniew Bodek int bgx_fdt_init_phy(struct bgx *);
702306b72aSZbigniew Bodek 
712306b72aSZbigniew Bodek static void
bgx_fdt_get_macaddr(phandle_t phy,uint8_t * hwaddr)722306b72aSZbigniew Bodek bgx_fdt_get_macaddr(phandle_t phy, uint8_t *hwaddr)
732306b72aSZbigniew Bodek {
742306b72aSZbigniew Bodek 	uint8_t addr[ETHER_ADDR_LEN];
752306b72aSZbigniew Bodek 
762306b72aSZbigniew Bodek 	if (OF_getprop(phy, "local-mac-address", addr, ETHER_ADDR_LEN) == -1) {
772306b72aSZbigniew Bodek 		/* Missing MAC address should be marked by clearing it */
782306b72aSZbigniew Bodek 		memset(hwaddr, 0, ETHER_ADDR_LEN);
792306b72aSZbigniew Bodek 	} else
802306b72aSZbigniew Bodek 		memcpy(hwaddr, addr, ETHER_ADDR_LEN);
812306b72aSZbigniew Bodek }
822306b72aSZbigniew Bodek 
832306b72aSZbigniew Bodek static boolean_t
bgx_fdt_phy_mode_match(struct bgx * bgx,char * qlm_mode,ssize_t size)8419fbe8bdSZbigniew Bodek bgx_fdt_phy_mode_match(struct bgx *bgx, char *qlm_mode, ssize_t size)
852306b72aSZbigniew Bodek {
8619fbe8bdSZbigniew Bodek 	const char *type;
8719fbe8bdSZbigniew Bodek 	ssize_t sz;
8819fbe8bdSZbigniew Bodek 	ssize_t offset;
892306b72aSZbigniew Bodek 
902306b72aSZbigniew Bodek 	switch (bgx->qlm_mode) {
912306b72aSZbigniew Bodek 	case QLM_MODE_SGMII:
9219fbe8bdSZbigniew Bodek 		type = "sgmii";
93c11862c0SAndrew Turner 		sz = sizeof("sgmii");
9419fbe8bdSZbigniew Bodek 		offset = size - sz;
952306b72aSZbigniew Bodek 		break;
962306b72aSZbigniew Bodek 	case QLM_MODE_XAUI_1X4:
9719fbe8bdSZbigniew Bodek 		type = "xaui";
98c11862c0SAndrew Turner 		sz = sizeof("xaui");
9919fbe8bdSZbigniew Bodek 		offset = size - sz;
10019fbe8bdSZbigniew Bodek 		if (offset < 0)
10119fbe8bdSZbigniew Bodek 			return (FALSE);
10219fbe8bdSZbigniew Bodek 		if (strncmp(&qlm_mode[offset], type, sz) == 0)
1032306b72aSZbigniew Bodek 			return (TRUE);
10419fbe8bdSZbigniew Bodek 		type = "dxaui";
105c11862c0SAndrew Turner 		sz = sizeof("dxaui");
10619fbe8bdSZbigniew Bodek 		offset = size - sz;
1072306b72aSZbigniew Bodek 		break;
1082306b72aSZbigniew Bodek 	case QLM_MODE_RXAUI_2X2:
10919fbe8bdSZbigniew Bodek 		type = "raui";
110c11862c0SAndrew Turner 		sz = sizeof("raui");
11119fbe8bdSZbigniew Bodek 		offset = size - sz;
1122306b72aSZbigniew Bodek 		break;
1132306b72aSZbigniew Bodek 	case QLM_MODE_XFI_4X1:
11419fbe8bdSZbigniew Bodek 		type = "xfi";
115c11862c0SAndrew Turner 		sz = sizeof("xfi");
11619fbe8bdSZbigniew Bodek 		offset = size - sz;
1172306b72aSZbigniew Bodek 		break;
1182306b72aSZbigniew Bodek 	case QLM_MODE_XLAUI_1X4:
11919fbe8bdSZbigniew Bodek 		type = "xlaui";
120c11862c0SAndrew Turner 		sz = sizeof("xlaui");
12119fbe8bdSZbigniew Bodek 		offset = size - sz;
1222306b72aSZbigniew Bodek 		break;
1232306b72aSZbigniew Bodek 	case QLM_MODE_10G_KR_4X1:
12419fbe8bdSZbigniew Bodek 		type = "xfi-10g-kr";
125c11862c0SAndrew Turner 		sz = sizeof("xfi-10g-kr");
12619fbe8bdSZbigniew Bodek 		offset = size - sz;
1272306b72aSZbigniew Bodek 		break;
1282306b72aSZbigniew Bodek 	case QLM_MODE_40G_KR4_1X4:
12919fbe8bdSZbigniew Bodek 		type = "xlaui-40g-kr";
130c11862c0SAndrew Turner 		sz = sizeof("xlaui-40g-kr");
13119fbe8bdSZbigniew Bodek 		offset = size - sz;
1322306b72aSZbigniew Bodek 		break;
1332306b72aSZbigniew Bodek 	default:
1342306b72aSZbigniew Bodek 		return (FALSE);
1352306b72aSZbigniew Bodek 	}
1362306b72aSZbigniew Bodek 
13719fbe8bdSZbigniew Bodek 	if (offset < 0)
13819fbe8bdSZbigniew Bodek 		return (FALSE);
13919fbe8bdSZbigniew Bodek 
14019fbe8bdSZbigniew Bodek 	if (strncmp(&qlm_mode[offset], type, sz) == 0)
14119fbe8bdSZbigniew Bodek 		return (TRUE);
14219fbe8bdSZbigniew Bodek 
14319fbe8bdSZbigniew Bodek 	return (FALSE);
14419fbe8bdSZbigniew Bodek }
14519fbe8bdSZbigniew Bodek 
14619fbe8bdSZbigniew Bodek static boolean_t
bgx_fdt_phy_name_match(struct bgx * bgx,char * phy_name,ssize_t size)14719fbe8bdSZbigniew Bodek bgx_fdt_phy_name_match(struct bgx *bgx, char *phy_name, ssize_t size)
14819fbe8bdSZbigniew Bodek {
14919fbe8bdSZbigniew Bodek 	const char *type;
15019fbe8bdSZbigniew Bodek 	ssize_t sz;
151*6074a3c0SMark Johnston 	char last;
15219fbe8bdSZbigniew Bodek 
15319fbe8bdSZbigniew Bodek 	switch (bgx->qlm_mode) {
15419fbe8bdSZbigniew Bodek 	case QLM_MODE_SGMII:
15519fbe8bdSZbigniew Bodek 		type = "sgmii";
156c11862c0SAndrew Turner 		sz = sizeof("sgmii");
15719fbe8bdSZbigniew Bodek 		break;
15819fbe8bdSZbigniew Bodek 	case QLM_MODE_XAUI_1X4:
15919fbe8bdSZbigniew Bodek 		type = "xaui";
160c11862c0SAndrew Turner 		sz = sizeof("xaui");
16119fbe8bdSZbigniew Bodek 		if (sz < size)
16219fbe8bdSZbigniew Bodek 			return (FALSE);
16319fbe8bdSZbigniew Bodek 		if (strncmp(phy_name, type, sz) == 0)
16419fbe8bdSZbigniew Bodek 			return (TRUE);
16519fbe8bdSZbigniew Bodek 		type = "dxaui";
166c11862c0SAndrew Turner 		sz = sizeof("dxaui");
16719fbe8bdSZbigniew Bodek 		break;
16819fbe8bdSZbigniew Bodek 	case QLM_MODE_RXAUI_2X2:
16919fbe8bdSZbigniew Bodek 		type = "raui";
170c11862c0SAndrew Turner 		sz = sizeof("raui");
17119fbe8bdSZbigniew Bodek 		break;
17219fbe8bdSZbigniew Bodek 	case QLM_MODE_XFI_4X1:
17319fbe8bdSZbigniew Bodek 		type = "xfi";
174c11862c0SAndrew Turner 		sz = sizeof("xfi");
17519fbe8bdSZbigniew Bodek 		break;
17619fbe8bdSZbigniew Bodek 	case QLM_MODE_XLAUI_1X4:
17719fbe8bdSZbigniew Bodek 		type = "xlaui";
178c11862c0SAndrew Turner 		sz = sizeof("xlaui");
17919fbe8bdSZbigniew Bodek 		break;
18019fbe8bdSZbigniew Bodek 	case QLM_MODE_10G_KR_4X1:
18119fbe8bdSZbigniew Bodek 		type = "xfi-10g-kr";
182c11862c0SAndrew Turner 		sz = sizeof("xfi-10g-kr");
18319fbe8bdSZbigniew Bodek 		break;
18419fbe8bdSZbigniew Bodek 	case QLM_MODE_40G_KR4_1X4:
18519fbe8bdSZbigniew Bodek 		type = "xlaui-40g-kr";
186c11862c0SAndrew Turner 		sz = sizeof("xlaui-40g-kr");
18719fbe8bdSZbigniew Bodek 		break;
18819fbe8bdSZbigniew Bodek 	default:
18919fbe8bdSZbigniew Bodek 		return (FALSE);
19019fbe8bdSZbigniew Bodek 	}
19119fbe8bdSZbigniew Bodek 
19219fbe8bdSZbigniew Bodek 	if (sz > size)
19319fbe8bdSZbigniew Bodek 		return (FALSE);
194*6074a3c0SMark Johnston 	if (strncmp(phy_name, type, sz - 1) == 0) {
195*6074a3c0SMark Johnston 		last = phy_name[sz - 1];
196*6074a3c0SMark Johnston 		if (last == '\0' || last == '@' || isdigit(last))
19719fbe8bdSZbigniew Bodek 			return (TRUE);
198*6074a3c0SMark Johnston 	}
1992306b72aSZbigniew Bodek 	return (FALSE);
2002306b72aSZbigniew Bodek }
2012306b72aSZbigniew Bodek 
202be624ad4SZbigniew Bodek static phandle_t
bgx_fdt_traverse_nodes(uint8_t unit,phandle_t start,char * name,size_t len)20319fbe8bdSZbigniew Bodek bgx_fdt_traverse_nodes(uint8_t unit, phandle_t start, char *name,
20419fbe8bdSZbigniew Bodek     size_t len)
205be624ad4SZbigniew Bodek {
206be624ad4SZbigniew Bodek 	phandle_t node, ret;
20719fbe8bdSZbigniew Bodek 	uint32_t *reg;
208be624ad4SZbigniew Bodek 	size_t buf_size;
20919fbe8bdSZbigniew Bodek 	ssize_t proplen;
210be624ad4SZbigniew Bodek 	char *node_name;
211be624ad4SZbigniew Bodek 	int err;
212be624ad4SZbigniew Bodek 
21319fbe8bdSZbigniew Bodek 	/*
21419fbe8bdSZbigniew Bodek 	 * Traverse all subordinate nodes of 'start' to find BGX instance.
21519fbe8bdSZbigniew Bodek 	 * This supports both old (by name) and new (by reg) methods.
21619fbe8bdSZbigniew Bodek 	 */
217be624ad4SZbigniew Bodek 	buf_size = sizeof(*node_name) * FDT_NAME_MAXLEN;
218be624ad4SZbigniew Bodek 	if (len > buf_size) {
219be624ad4SZbigniew Bodek 		/*
220be624ad4SZbigniew Bodek 		 * This is an erroneous situation since the string
221be624ad4SZbigniew Bodek 		 * to compare cannot be longer than FDT_NAME_MAXLEN.
222be624ad4SZbigniew Bodek 		 */
223be624ad4SZbigniew Bodek 		return (0);
224be624ad4SZbigniew Bodek 	}
225be624ad4SZbigniew Bodek 
226be624ad4SZbigniew Bodek 	node_name = malloc(buf_size, M_BGX, M_WAITOK);
227be624ad4SZbigniew Bodek 	for (node = OF_child(start); node != 0; node = OF_peer(node)) {
228be624ad4SZbigniew Bodek 		/* Clean-up the buffer */
229be624ad4SZbigniew Bodek 		memset(node_name, 0, buf_size);
230be624ad4SZbigniew Bodek 		/* Recurse to children */
231be624ad4SZbigniew Bodek 		if (OF_child(node) != 0) {
23219fbe8bdSZbigniew Bodek 			ret = bgx_fdt_traverse_nodes(unit, node, name, len);
233be624ad4SZbigniew Bodek 			if (ret != 0) {
234be624ad4SZbigniew Bodek 				free(node_name, M_BGX);
235be624ad4SZbigniew Bodek 				return (ret);
236be624ad4SZbigniew Bodek 			}
237be624ad4SZbigniew Bodek 		}
23819fbe8bdSZbigniew Bodek 		/*
23919fbe8bdSZbigniew Bodek 		 * Old way - by name
24019fbe8bdSZbigniew Bodek 		 */
24119fbe8bdSZbigniew Bodek 		proplen = OF_getproplen(node, "name");
24219fbe8bdSZbigniew Bodek 		if ((proplen <= 0) || (proplen < len))
24319fbe8bdSZbigniew Bodek 			continue;
24419fbe8bdSZbigniew Bodek 
24519fbe8bdSZbigniew Bodek 		err = OF_getprop(node, "name", node_name, proplen);
24619fbe8bdSZbigniew Bodek 		if (err <= 0)
24719fbe8bdSZbigniew Bodek 			continue;
24819fbe8bdSZbigniew Bodek 
24919fbe8bdSZbigniew Bodek 		if (strncmp(node_name, name, len) == 0) {
250be624ad4SZbigniew Bodek 			free(node_name, M_BGX);
251be624ad4SZbigniew Bodek 			return (node);
252be624ad4SZbigniew Bodek 		}
25319fbe8bdSZbigniew Bodek 		/*
25419fbe8bdSZbigniew Bodek 		 * New way - by reg
25519fbe8bdSZbigniew Bodek 		 */
25619fbe8bdSZbigniew Bodek 		/* Check if even BGX */
25719fbe8bdSZbigniew Bodek 		if (strncmp(node_name,
25819fbe8bdSZbigniew Bodek 		    BGX_NODE_NAME, sizeof(BGX_NODE_NAME) - 1) != 0)
25919fbe8bdSZbigniew Bodek 			continue;
26019fbe8bdSZbigniew Bodek 		/* Get reg */
261f7604b1bSOleksandr Tymoshenko 		err = OF_getencprop_alloc_multi(node, "reg", sizeof(*reg),
26219fbe8bdSZbigniew Bodek 		    (void **)&reg);
26319fbe8bdSZbigniew Bodek 		if (err == -1) {
26419fbe8bdSZbigniew Bodek 			free(reg, M_OFWPROP);
26519fbe8bdSZbigniew Bodek 			continue;
26619fbe8bdSZbigniew Bodek 		}
26719fbe8bdSZbigniew Bodek 
26819fbe8bdSZbigniew Bodek 		/* Match BGX device function */
26919fbe8bdSZbigniew Bodek 		if ((BGX_DEVFN_0 + unit) == (reg[0] >> 8)) {
27019fbe8bdSZbigniew Bodek 			free(reg, M_OFWPROP);
27119fbe8bdSZbigniew Bodek 			free(node_name, M_BGX);
27219fbe8bdSZbigniew Bodek 			return (node);
27319fbe8bdSZbigniew Bodek 		}
27419fbe8bdSZbigniew Bodek 		free(reg, M_OFWPROP);
275be624ad4SZbigniew Bodek 	}
276be624ad4SZbigniew Bodek 	free(node_name, M_BGX);
277be624ad4SZbigniew Bodek 
278be624ad4SZbigniew Bodek 	return (0);
279be624ad4SZbigniew Bodek }
280be624ad4SZbigniew Bodek 
281be624ad4SZbigniew Bodek /*
282be624ad4SZbigniew Bodek  * Similar functionality to pci_find_pcie_root_port()
283be624ad4SZbigniew Bodek  * but this one works for ThunderX.
284be624ad4SZbigniew Bodek  */
285be624ad4SZbigniew Bodek static device_t
bgx_find_root_pcib(device_t dev)286be624ad4SZbigniew Bodek bgx_find_root_pcib(device_t dev)
287be624ad4SZbigniew Bodek {
288be624ad4SZbigniew Bodek 	devclass_t pci_class;
289be624ad4SZbigniew Bodek 	device_t pcib, bus;
290be624ad4SZbigniew Bodek 
291be624ad4SZbigniew Bodek 	pci_class = devclass_find("pci");
292be624ad4SZbigniew Bodek 	KASSERT(device_get_devclass(device_get_parent(dev)) == pci_class,
293be624ad4SZbigniew Bodek 	    ("%s: non-pci device %s", __func__, device_get_nameunit(dev)));
294be624ad4SZbigniew Bodek 
295be624ad4SZbigniew Bodek 	/* Walk the bridge hierarchy until we find a non-PCI device */
296be624ad4SZbigniew Bodek 	for (;;) {
297be624ad4SZbigniew Bodek 		bus = device_get_parent(dev);
298be624ad4SZbigniew Bodek 		KASSERT(bus != NULL, ("%s: null parent of %s", __func__,
299be624ad4SZbigniew Bodek 		    device_get_nameunit(dev)));
300be624ad4SZbigniew Bodek 
301be624ad4SZbigniew Bodek 		if (device_get_devclass(bus) != pci_class)
302be624ad4SZbigniew Bodek 			return (NULL);
303be624ad4SZbigniew Bodek 
304be624ad4SZbigniew Bodek 		pcib = device_get_parent(bus);
305be624ad4SZbigniew Bodek 		KASSERT(pcib != NULL, ("%s: null bridge of %s", __func__,
306be624ad4SZbigniew Bodek 		    device_get_nameunit(bus)));
307be624ad4SZbigniew Bodek 
308be624ad4SZbigniew Bodek 		/*
309be624ad4SZbigniew Bodek 		 * If the parent of this PCIB is not PCI
310be624ad4SZbigniew Bodek 		 * then we found our root PCIB.
311be624ad4SZbigniew Bodek 		 */
312be624ad4SZbigniew Bodek 		if (device_get_devclass(device_get_parent(pcib)) != pci_class)
313be624ad4SZbigniew Bodek 			return (pcib);
314be624ad4SZbigniew Bodek 
315be624ad4SZbigniew Bodek 		dev = pcib;
316be624ad4SZbigniew Bodek 	}
317be624ad4SZbigniew Bodek }
318be624ad4SZbigniew Bodek 
319be624ad4SZbigniew Bodek static __inline phandle_t
bgx_fdt_find_node(struct bgx * bgx)320be624ad4SZbigniew Bodek bgx_fdt_find_node(struct bgx *bgx)
321be624ad4SZbigniew Bodek {
322be624ad4SZbigniew Bodek 	device_t root_pcib;
323be624ad4SZbigniew Bodek 	phandle_t node;
324be624ad4SZbigniew Bodek 	char *bgx_sel;
325be624ad4SZbigniew Bodek 	size_t len;
326be624ad4SZbigniew Bodek 
327be624ad4SZbigniew Bodek 	KASSERT(bgx->bgx_id <= BGX_MAXID,
328be624ad4SZbigniew Bodek 	    ("Invalid BGX ID: %d, max: %d", bgx->bgx_id, BGX_MAXID));
329be624ad4SZbigniew Bodek 
330be624ad4SZbigniew Bodek 	len = sizeof(BGX_NODE_NAME) + 1; /* <bgx_name>+<digit>+<\0> */
331be624ad4SZbigniew Bodek 	/* Allocate memory for BGX node name + "/" character */
332be624ad4SZbigniew Bodek 	bgx_sel = malloc(sizeof(*bgx_sel) * (len + 1), M_BGX,
333be624ad4SZbigniew Bodek 	    M_ZERO | M_WAITOK);
334be624ad4SZbigniew Bodek 
335be624ad4SZbigniew Bodek 	/* Prepare node's name */
336be624ad4SZbigniew Bodek 	snprintf(bgx_sel, len + 1, "/"BGX_NODE_NAME"%d", bgx->bgx_id);
337be624ad4SZbigniew Bodek 	/* First try the root node */
338be624ad4SZbigniew Bodek 	node =  OF_finddevice(bgx_sel);
339108117ccSOleksandr Tymoshenko 	if (node != -1) {
340be624ad4SZbigniew Bodek 		/* Found relevant node */
341be624ad4SZbigniew Bodek 		goto out;
342be624ad4SZbigniew Bodek 	}
343be624ad4SZbigniew Bodek 	/*
344be624ad4SZbigniew Bodek 	 * Clean-up and try to find BGX in DT
345be624ad4SZbigniew Bodek 	 * starting from the parent PCI bridge node.
346be624ad4SZbigniew Bodek 	 */
347be624ad4SZbigniew Bodek 	memset(bgx_sel, 0, sizeof(*bgx_sel) * (len + 1));
348be624ad4SZbigniew Bodek 	snprintf(bgx_sel, len, BGX_NODE_NAME"%d", bgx->bgx_id);
349be624ad4SZbigniew Bodek 
350be624ad4SZbigniew Bodek 	/* Find PCI bridge that we are connected to */
351be624ad4SZbigniew Bodek 
352be624ad4SZbigniew Bodek 	root_pcib = bgx_find_root_pcib(bgx->dev);
353be624ad4SZbigniew Bodek 	if (root_pcib == NULL) {
354be624ad4SZbigniew Bodek 		device_printf(bgx->dev, "Unable to find BGX root bridge\n");
355be624ad4SZbigniew Bodek 		node = 0;
356be624ad4SZbigniew Bodek 		goto out;
357be624ad4SZbigniew Bodek 	}
358be624ad4SZbigniew Bodek 
359be624ad4SZbigniew Bodek 	node = ofw_bus_get_node(root_pcib);
360db7cfc19SZbigniew Bodek 	if ((int)node <= 0) {
361be624ad4SZbigniew Bodek 		device_printf(bgx->dev, "No parent FDT node for BGX\n");
362be624ad4SZbigniew Bodek 		goto out;
363be624ad4SZbigniew Bodek 	}
364be624ad4SZbigniew Bodek 
36519fbe8bdSZbigniew Bodek 	node = bgx_fdt_traverse_nodes(bgx->bgx_id, node, bgx_sel, len);
366be624ad4SZbigniew Bodek out:
367be624ad4SZbigniew Bodek 	free(bgx_sel, M_BGX);
368be624ad4SZbigniew Bodek 	return (node);
369be624ad4SZbigniew Bodek }
370be624ad4SZbigniew Bodek 
3712306b72aSZbigniew Bodek int
bgx_fdt_init_phy(struct bgx * bgx)3722306b72aSZbigniew Bodek bgx_fdt_init_phy(struct bgx *bgx)
3732306b72aSZbigniew Bodek {
37419fbe8bdSZbigniew Bodek 	char *node_name;
3752306b72aSZbigniew Bodek 	phandle_t node, child;
3762306b72aSZbigniew Bodek 	phandle_t phy, mdio;
37719fbe8bdSZbigniew Bodek 	ssize_t len;
3782306b72aSZbigniew Bodek 	uint8_t lmac;
3792306b72aSZbigniew Bodek 	char qlm_mode[CONN_TYPE_MAXLEN];
3802306b72aSZbigniew Bodek 
381be624ad4SZbigniew Bodek 	node = bgx_fdt_find_node(bgx);
382be624ad4SZbigniew Bodek 	if (node == 0) {
3832306b72aSZbigniew Bodek 		device_printf(bgx->dev,
384be624ad4SZbigniew Bodek 		    "Could not find bgx%d node in FDT\n", bgx->bgx_id);
3852306b72aSZbigniew Bodek 		return (ENXIO);
3862306b72aSZbigniew Bodek 	}
3872306b72aSZbigniew Bodek 
388be624ad4SZbigniew Bodek 	lmac = 0;
3892306b72aSZbigniew Bodek 	for (child = OF_child(node); child > 0; child = OF_peer(child)) {
39019fbe8bdSZbigniew Bodek 		len = OF_getprop(child, "qlm-mode", qlm_mode, sizeof(qlm_mode));
39119fbe8bdSZbigniew Bodek 		if (len > 0) {
39219fbe8bdSZbigniew Bodek 			if (!bgx_fdt_phy_mode_match(bgx, qlm_mode, len)) {
3932306b72aSZbigniew Bodek 				/*
3942306b72aSZbigniew Bodek 				 * Connection type not match with BGX mode.
3952306b72aSZbigniew Bodek 				 */
3962306b72aSZbigniew Bodek 				continue;
3972306b72aSZbigniew Bodek 			}
39819fbe8bdSZbigniew Bodek 		} else {
399217d17bcSOleksandr Tymoshenko 			len = OF_getprop_alloc(child, "name",
40019fbe8bdSZbigniew Bodek 			    (void **)&node_name);
40119fbe8bdSZbigniew Bodek 			if (len <= 0) {
40219fbe8bdSZbigniew Bodek 				continue;
40319fbe8bdSZbigniew Bodek 			}
4042306b72aSZbigniew Bodek 
40519fbe8bdSZbigniew Bodek 			if (!bgx_fdt_phy_name_match(bgx, node_name, len)) {
40619fbe8bdSZbigniew Bodek 				free(node_name, M_OFWPROP);
40719fbe8bdSZbigniew Bodek 				continue;
40819fbe8bdSZbigniew Bodek 			}
40919fbe8bdSZbigniew Bodek 			free(node_name, M_OFWPROP);
41019fbe8bdSZbigniew Bodek 		}
4112306b72aSZbigniew Bodek 
4122306b72aSZbigniew Bodek 		/* Acquire PHY address */
413316e15b4SZbigniew Bodek 		if (OF_getencprop(child, "reg", &bgx->lmac[lmac].phyaddr,
4142306b72aSZbigniew Bodek 		    sizeof(bgx->lmac[lmac].phyaddr)) <= 0) {
4152306b72aSZbigniew Bodek 			if (bootverbose) {
4162306b72aSZbigniew Bodek 				device_printf(bgx->dev,
4172306b72aSZbigniew Bodek 				    "Could not retrieve PHY address\n");
4182306b72aSZbigniew Bodek 			}
4192306b72aSZbigniew Bodek 			bgx->lmac[lmac].phyaddr = MII_PHY_ANY;
4202306b72aSZbigniew Bodek 		}
4212306b72aSZbigniew Bodek 
422316e15b4SZbigniew Bodek 		if (OF_getencprop(child, "phy-handle", &phy,
423316e15b4SZbigniew Bodek 		    sizeof(phy)) <= 0) {
424316e15b4SZbigniew Bodek 			if (bootverbose) {
425316e15b4SZbigniew Bodek 				device_printf(bgx->dev,
426316e15b4SZbigniew Bodek 				    "No phy-handle in PHY node. Skipping...\n");
427316e15b4SZbigniew Bodek 			}
428316e15b4SZbigniew Bodek 			continue;
429316e15b4SZbigniew Bodek 		}
430316e15b4SZbigniew Bodek 		phy = OF_instance_to_package(phy);
4312306b72aSZbigniew Bodek 		/*
4322306b72aSZbigniew Bodek 		 * Get PHY interface (MDIO bus) device.
4332306b72aSZbigniew Bodek 		 * Driver must be already attached.
4342306b72aSZbigniew Bodek 		 */
4352306b72aSZbigniew Bodek 		mdio = OF_parent(phy);
4362306b72aSZbigniew Bodek 		bgx->lmac[lmac].phy_if_dev =
4372306b72aSZbigniew Bodek 		    OF_device_from_xref(OF_xref_from_node(mdio));
4382306b72aSZbigniew Bodek 		if (bgx->lmac[lmac].phy_if_dev == NULL) {
4392306b72aSZbigniew Bodek 			if (bootverbose) {
4402306b72aSZbigniew Bodek 				device_printf(bgx->dev,
4412306b72aSZbigniew Bodek 				    "Could not find interface to PHY\n");
4422306b72aSZbigniew Bodek 			}
4432306b72aSZbigniew Bodek 			continue;
4442306b72aSZbigniew Bodek 		}
4452306b72aSZbigniew Bodek 
4462306b72aSZbigniew Bodek 		/* Get mac address from FDT */
447316e15b4SZbigniew Bodek 		bgx_fdt_get_macaddr(child, bgx->lmac[lmac].mac);
4482306b72aSZbigniew Bodek 
4492306b72aSZbigniew Bodek 		bgx->lmac[lmac].lmacid = lmac;
4502306b72aSZbigniew Bodek 		lmac++;
4512306b72aSZbigniew Bodek 		if (lmac == MAX_LMAC_PER_BGX)
4522306b72aSZbigniew Bodek 			break;
4532306b72aSZbigniew Bodek 	}
4542306b72aSZbigniew Bodek 	if (lmac == 0) {
4552306b72aSZbigniew Bodek 		device_printf(bgx->dev, "Could not find matching PHY\n");
4562306b72aSZbigniew Bodek 		return (ENXIO);
4572306b72aSZbigniew Bodek 	}
4582306b72aSZbigniew Bodek 
4592306b72aSZbigniew Bodek 	return (0);
4602306b72aSZbigniew Bodek }
461