| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Infrastructure to handle all PHY devices connected to a given netdev,
- * either directly or indirectly attached.
- *
- * Copyright (c) 2023 Maxime Chevallier<maxime.chevallier@bootlin.com>
- */
- #include <linux/phy_link_topology.h>
- #include <linux/phy.h>
- #include <linux/rtnetlink.h>
- #include <linux/xarray.h>
- static int netdev_alloc_phy_link_topology(struct net_device *dev)
- {
- struct phy_link_topology *topo;
- topo = kzalloc_obj(*topo);
- if (!topo)
- return -ENOMEM;
- xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
- topo->next_phy_index = 1;
- dev->link_topo = topo;
- return 0;
- }
- int phy_link_topo_add_phy(struct net_device *dev,
- struct phy_device *phy,
- enum phy_upstream upt, void *upstream)
- {
- struct phy_link_topology *topo = dev->link_topo;
- struct phy_device_node *pdn;
- int ret;
- if (!topo) {
- ret = netdev_alloc_phy_link_topology(dev);
- if (ret)
- return ret;
- topo = dev->link_topo;
- }
- pdn = kzalloc_obj(*pdn);
- if (!pdn)
- return -ENOMEM;
- pdn->phy = phy;
- switch (upt) {
- case PHY_UPSTREAM_MAC:
- pdn->upstream.netdev = (struct net_device *)upstream;
- if (phy_on_sfp(phy))
- pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
- break;
- case PHY_UPSTREAM_PHY:
- pdn->upstream.phydev = (struct phy_device *)upstream;
- if (phy_on_sfp(phy))
- pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
- pdn->upstream_type = upt;
- /* Attempt to re-use a previously allocated phy_index */
- if (phy->phyindex)
- ret = xa_insert(&topo->phys, phy->phyindex, pdn, GFP_KERNEL);
- else
- ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn,
- xa_limit_32b, &topo->next_phy_index,
- GFP_KERNEL);
- if (ret < 0)
- goto err;
- return 0;
- err:
- kfree(pdn);
- return ret;
- }
- EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
- void phy_link_topo_del_phy(struct net_device *dev,
- struct phy_device *phy)
- {
- struct phy_link_topology *topo = dev->link_topo;
- struct phy_device_node *pdn;
- if (!topo)
- return;
- pdn = xa_erase(&topo->phys, phy->phyindex);
- /* We delete the PHY from the topology, however we don't re-set the
- * phy->phyindex field. If the PHY isn't gone, we can re-assign it the
- * same index next time it's added back to the topology
- */
- kfree(pdn);
- }
- EXPORT_SYMBOL_GPL(phy_link_topo_del_phy);
|