xref: /linux/drivers/net/phy/phy_link_topology.c (revision 7a4ffec9fd54ea27395e24dff726dbf58e2fe06b)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Infrastructure to handle all PHY devices connected to a given netdev,
4  * either directly or indirectly attached.
5  *
6  * Copyright (c) 2023 Maxime Chevallier<maxime.chevallier@bootlin.com>
7  */
8 
9 #include <linux/phy_link_topology.h>
10 #include <linux/phy.h>
11 #include <linux/rtnetlink.h>
12 #include <linux/xarray.h>
13 
14 static int netdev_alloc_phy_link_topology(struct net_device *dev)
15 {
16 	struct phy_link_topology *topo;
17 
18 	topo = kzalloc(sizeof(*topo), GFP_KERNEL);
19 	if (!topo)
20 		return -ENOMEM;
21 
22 	xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
23 	topo->next_phy_index = 1;
24 
25 	dev->link_topo = topo;
26 
27 	return 0;
28 }
29 
30 int phy_link_topo_add_phy(struct net_device *dev,
31 			  struct phy_device *phy,
32 			  enum phy_upstream upt, void *upstream)
33 {
34 	struct phy_link_topology *topo = dev->link_topo;
35 	struct phy_device_node *pdn;
36 	int ret;
37 
38 	if (!topo) {
39 		ret = netdev_alloc_phy_link_topology(dev);
40 		if (ret)
41 			return ret;
42 
43 		topo = dev->link_topo;
44 	}
45 
46 	pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
47 	if (!pdn)
48 		return -ENOMEM;
49 
50 	pdn->phy = phy;
51 	switch (upt) {
52 	case PHY_UPSTREAM_MAC:
53 		pdn->upstream.netdev = (struct net_device *)upstream;
54 		if (phy_on_sfp(phy))
55 			pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
56 		break;
57 	case PHY_UPSTREAM_PHY:
58 		pdn->upstream.phydev = (struct phy_device *)upstream;
59 		if (phy_on_sfp(phy))
60 			pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
61 		break;
62 	default:
63 		ret = -EINVAL;
64 		goto err;
65 	}
66 	pdn->upstream_type = upt;
67 
68 	/* Attempt to re-use a previously allocated phy_index */
69 	if (phy->phyindex)
70 		ret = xa_insert(&topo->phys, phy->phyindex, pdn, GFP_KERNEL);
71 	else
72 		ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn,
73 				      xa_limit_32b, &topo->next_phy_index,
74 				      GFP_KERNEL);
75 
76 	if (ret)
77 		goto err;
78 
79 	return 0;
80 
81 err:
82 	kfree(pdn);
83 	return ret;
84 }
85 EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
86 
87 void phy_link_topo_del_phy(struct net_device *dev,
88 			   struct phy_device *phy)
89 {
90 	struct phy_link_topology *topo = dev->link_topo;
91 	struct phy_device_node *pdn;
92 
93 	if (!topo)
94 		return;
95 
96 	pdn = xa_erase(&topo->phys, phy->phyindex);
97 
98 	/* We delete the PHY from the topology, however we don't re-set the
99 	 * phy->phyindex field. If the PHY isn't gone, we can re-assign it the
100 	 * same index next time it's added back to the topology
101 	 */
102 
103 	kfree(pdn);
104 }
105 EXPORT_SYMBOL_GPL(phy_link_topo_del_phy);
106