xref: /linux/drivers/net/phy/phy_link_topology.c (revision ef9226cd56b718c79184a3466d32984a51cb449c)
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/netdevice.h>
11 #include <linux/phy.h>
12 #include <linux/rtnetlink.h>
13 #include <linux/xarray.h>
14 
15 struct phy_link_topology *phy_link_topo_create(struct net_device *dev)
16 {
17 	struct phy_link_topology *topo;
18 
19 	topo = kzalloc(sizeof(*topo), GFP_KERNEL);
20 	if (!topo)
21 		return ERR_PTR(-ENOMEM);
22 
23 	xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
24 	topo->next_phy_index = 1;
25 
26 	return topo;
27 }
28 
29 void phy_link_topo_destroy(struct phy_link_topology *topo)
30 {
31 	if (!topo)
32 		return;
33 
34 	xa_destroy(&topo->phys);
35 	kfree(topo);
36 }
37 
38 int phy_link_topo_add_phy(struct phy_link_topology *topo,
39 			  struct phy_device *phy,
40 			  enum phy_upstream upt, void *upstream)
41 {
42 	struct phy_device_node *pdn;
43 	int ret;
44 
45 	pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
46 	if (!pdn)
47 		return -ENOMEM;
48 
49 	pdn->phy = phy;
50 	switch (upt) {
51 	case PHY_UPSTREAM_MAC:
52 		pdn->upstream.netdev = (struct net_device *)upstream;
53 		if (phy_on_sfp(phy))
54 			pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
55 		break;
56 	case PHY_UPSTREAM_PHY:
57 		pdn->upstream.phydev = (struct phy_device *)upstream;
58 		if (phy_on_sfp(phy))
59 			pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
60 		break;
61 	default:
62 		ret = -EINVAL;
63 		goto err;
64 	}
65 	pdn->upstream_type = upt;
66 
67 	/* Attempt to re-use a previously allocated phy_index */
68 	if (phy->phyindex) {
69 		ret = xa_insert(&topo->phys, phy->phyindex, pdn, GFP_KERNEL);
70 
71 		/* Errors could be either -ENOMEM or -EBUSY. If the phy has an
72 		 * index, and there's another entry at the same index, this is
73 		 * unexpected and we still error-out
74 		 */
75 		if (ret)
76 			goto err;
77 		return 0;
78 	}
79 
80 	ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn, xa_limit_32b,
81 			      &topo->next_phy_index, GFP_KERNEL);
82 	if (ret)
83 		goto err;
84 
85 	return 0;
86 
87 err:
88 	kfree(pdn);
89 	return ret;
90 }
91 EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
92 
93 void phy_link_topo_del_phy(struct phy_link_topology *topo,
94 			   struct phy_device *phy)
95 {
96 	struct phy_device_node *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