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