Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: phy: Introduce PHY ports representation

Ethernet provides a wide variety of layer 1 protocols and standards for
data transmission. The front-facing ports of an interface have their own
complexity and configurability.

Introduce a representation of these front-facing ports. The current code
is minimalistic and only support ports controlled by PHY devices, but
the plan is to extend that to SFP as well as raw Ethernet MACs that
don't use PHY devices.

This minimal port representation allows describing the media and number
of pairs of a BaseT port. From that information, we can derive the
linkmodes usable on the port, which can be used to limit the
capabilities of an interface.

For now, the port pairs and medium is derived from devicetree, defined
by the PHY driver, or populated with default values (as we assume that
all PHYs expose at least one port).

The typical example is 100M ethernet. 100BaseTX works using only 2
pairs on a Cat 5 cables. However, in the situation where a 10/100/1000
capable PHY is wired to its RJ45 port through 2 pairs only, we have no
way of detecting that. The "max-speed" DT property can be used, but a
more accurate representation can be used :

mdi {
connector-0 {
media = "BaseT";
pairs = <2>;
};
};

From that information, we can derive the max speed reachable on the
port.

Another benefit of having that is to avoid vendor-specific DT properties
(micrel,fiber-mode or ti,fiber-mode).

This basic representation is meant to be expanded, by the introduction
of port ops, userspace listing of ports, and support for multi-port
devices.

Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20260108080041.553250-4-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Maxime Chevallier and committed by
Jakub Kicinski
589e934d 3f25ff74

+610 -1
+7
MAINTAINERS
··· 18218 18218 F: include/linux/phy_link_topology.h 18219 18219 F: net/ethtool/phy.c 18220 18220 18221 + NETWORKING [ETHTOOL PHY PORT] 18222 + M: Maxime Chevallier <maxime.chevallier@bootlin.com> 18223 + F: Documentation/devicetree/bindings/net/ethernet-connector.yaml 18224 + F: drivers/net/phy/phy_port.c 18225 + F: include/linux/phy_port.h 18226 + K: struct\s+phy_port|phy_port_ 18227 + 18221 18228 NETWORKING [GENERAL] 18222 18229 M: "David S. Miller" <davem@davemloft.net> 18223 18230 M: Eric Dumazet <edumazet@google.com>
+1 -1
drivers/net/phy/Makefile
··· 3 3 4 4 libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \ 5 5 linkmode.o phy_link_topology.o \ 6 - phy_caps.o mdio_bus_provider.o 6 + phy_caps.o mdio_bus_provider.o phy_port.o 7 7 mdio-bus-y += mdio_bus.o mdio_device.o 8 8 9 9 ifdef CONFIG_PHYLIB
+5
drivers/net/phy/phy-caps.h
··· 61 61 phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported, 62 62 bool exact); 63 63 64 + void phy_caps_medium_get_supported(unsigned long *supported, 65 + enum ethtool_link_medium medium, 66 + int lanes); 67 + u32 phy_caps_mediums_from_linkmodes(unsigned long *linkmodes); 68 + 64 69 #endif /* __PHY_CAPS_H */
+6
drivers/net/phy/phy-core.c
··· 4 4 */ 5 5 #include <linux/export.h> 6 6 #include <linux/phy.h> 7 + #include <linux/phy_port.h> 7 8 #include <linux/of.h> 8 9 9 10 #include "phylib.h" ··· 209 208 210 209 static void __set_phy_supported(struct phy_device *phydev, u32 max_speed) 211 210 { 211 + struct phy_port *port; 212 + 212 213 phy_caps_linkmode_max_speed(max_speed, phydev->supported); 214 + 215 + phy_for_each_port(phydev, port) 216 + phy_caps_linkmode_max_speed(max_speed, port->supported); 213 217 } 214 218 215 219 /**
+57
drivers/net/phy/phy_caps.c
··· 386 386 return link_caps; 387 387 } 388 388 EXPORT_SYMBOL_GPL(phy_caps_from_interface); 389 + 390 + /** 391 + * phy_caps_medium_get_supported() - Returns linkmodes supported on a given medium 392 + * @supported: After this call, contains all possible linkmodes on a given medium, 393 + * and with the given number of pairs, or less. 394 + * @medium: The medium to get the support from 395 + * @pairs: The number of pairs used on the given medium. Only relevant for modes 396 + * that support this notion, such as BaseT. Pass 0 if not applicable. 397 + * 398 + * If no match exists, the supported field is left untouched. 399 + */ 400 + void phy_caps_medium_get_supported(unsigned long *supported, 401 + enum ethtool_link_medium medium, 402 + int pairs) 403 + { 404 + int i; 405 + 406 + for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) { 407 + /* Special bits such as Autoneg, Pause, Asym_pause, etc. are 408 + * set and will be masked away by the port parent. 409 + */ 410 + if (link_mode_params[i].mediums == BIT(ETHTOOL_LINK_MEDIUM_NONE)) { 411 + linkmode_set_bit(i, supported); 412 + continue; 413 + } 414 + 415 + /* If this medium matches, and had a non-zero min-pairs */ 416 + if (link_mode_params[i].mediums & BIT(medium) && 417 + (!link_mode_params[i].min_pairs || 418 + (link_mode_params[i].min_pairs <= pairs && 419 + link_mode_params[i].pairs >= pairs))) 420 + linkmode_set_bit(i, supported); 421 + } 422 + } 423 + EXPORT_SYMBOL_GPL(phy_caps_medium_get_supported); 424 + 425 + /** 426 + * phy_caps_mediums_from_linkmodes() - Get all mediums from a linkmodes list 427 + * @linkmodes: A bitset of linkmodes to get the mediums from 428 + * 429 + * Returns: A bitset of ETHTOOL_MEDIUM_XXX values corresponding to all medium 430 + * types in the linkmodes list 431 + */ 432 + u32 phy_caps_mediums_from_linkmodes(unsigned long *linkmodes) 433 + { 434 + const struct link_mode_info *linkmode; 435 + u32 mediums = 0; 436 + int i; 437 + 438 + for_each_set_bit(i, linkmodes, __ETHTOOL_LINK_MODE_MASK_NBITS) { 439 + linkmode = &link_mode_params[i]; 440 + mediums |= linkmode->mediums; 441 + } 442 + 443 + return mediums; 444 + } 445 + EXPORT_SYMBOL_GPL(phy_caps_mediums_from_linkmodes);
+192
drivers/net/phy/phy_device.c
··· 30 30 #include <linux/phylib_stubs.h> 31 31 #include <linux/phy_led_triggers.h> 32 32 #include <linux/phy_link_topology.h> 33 + #include <linux/phy_port.h> 33 34 #include <linux/pse-pd/pse.h> 34 35 #include <linux/property.h> 35 36 #include <linux/ptp_clock_kernel.h> ··· 846 845 847 846 dev->state = PHY_DOWN; 848 847 INIT_LIST_HEAD(&dev->leds); 848 + INIT_LIST_HEAD(&dev->ports); 849 + 850 + /* The driver's probe function must change that to the real number 851 + * of ports possible on the PHY. We assume by default we are dealing 852 + * with a single-port PHY 853 + */ 854 + dev->max_n_ports = 1; 849 855 850 856 mutex_init(&dev->lock); 851 857 INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); ··· 1597 1589 phydev->sfp_bus_attached = false; 1598 1590 } 1599 1591 EXPORT_SYMBOL(phy_sfp_detach); 1592 + 1593 + static int phy_add_port(struct phy_device *phydev, struct phy_port *port) 1594 + { 1595 + int ret = 0; 1596 + 1597 + if (phydev->n_ports == phydev->max_n_ports) 1598 + return -EBUSY; 1599 + 1600 + /* We set all ports as active by default, PHY drivers may deactivate 1601 + * them (when unused) 1602 + */ 1603 + port->active = true; 1604 + 1605 + if (port->is_mii) { 1606 + if (phydev->drv && phydev->drv->attach_mii_port) 1607 + ret = phydev->drv->attach_mii_port(phydev, port); 1608 + } else { 1609 + if (phydev->drv && phydev->drv->attach_mdi_port) 1610 + ret = phydev->drv->attach_mdi_port(phydev, port); 1611 + } 1612 + 1613 + if (ret) 1614 + return ret; 1615 + 1616 + /* The PHY driver might have added, removed or set medium/pairs info, 1617 + * so update the port supported accordingly. 1618 + */ 1619 + phy_port_update_supported(port); 1620 + 1621 + list_add(&port->head, &phydev->ports); 1622 + 1623 + phydev->n_ports++; 1624 + 1625 + return 0; 1626 + } 1627 + 1628 + static void phy_del_port(struct phy_device *phydev, struct phy_port *port) 1629 + { 1630 + if (!phydev->n_ports) 1631 + return; 1632 + 1633 + list_del(&port->head); 1634 + 1635 + phydev->n_ports--; 1636 + } 1600 1637 1601 1638 /** 1602 1639 * phy_sfp_probe - probe for a SFP cage attached to this PHY device ··· 3378 3325 return 0; 3379 3326 } 3380 3327 3328 + static void phy_cleanup_ports(struct phy_device *phydev) 3329 + { 3330 + struct phy_port *tmp, *port; 3331 + 3332 + list_for_each_entry_safe(port, tmp, &phydev->ports, head) { 3333 + phy_del_port(phydev, port); 3334 + phy_port_destroy(port); 3335 + } 3336 + } 3337 + 3338 + static int phy_default_setup_single_port(struct phy_device *phydev) 3339 + { 3340 + struct phy_port *port = phy_port_alloc(); 3341 + unsigned long mode; 3342 + 3343 + if (!port) 3344 + return -ENOMEM; 3345 + 3346 + port->parent_type = PHY_PORT_PHY; 3347 + port->phy = phydev; 3348 + 3349 + /* Let the PHY driver know that this port was never described anywhere. 3350 + * This is the usual case, where we assume single-port PHY devices with 3351 + * no SFP. In that case, the port supports exactly the same thing as 3352 + * the PHY itself. 3353 + * 3354 + * However, this can also be because we have a combo-port PHY, with 3355 + * only one port described in DT, through SFP for example. 3356 + * 3357 + * In that case, the PHY driver will be in charge of saying what we can 3358 + * do on that non-represented port. 3359 + */ 3360 + port->not_described = true; 3361 + linkmode_copy(port->supported, phydev->supported); 3362 + port->mediums = phy_caps_mediums_from_linkmodes(port->supported); 3363 + 3364 + for_each_set_bit(mode, port->supported, __ETHTOOL_LINK_MODE_MASK_NBITS) 3365 + port->pairs = max_t(int, port->pairs, 3366 + ethtool_linkmode_n_pairs(mode)); 3367 + 3368 + phy_add_port(phydev, port); 3369 + 3370 + return 0; 3371 + } 3372 + 3373 + static int of_phy_ports(struct phy_device *phydev) 3374 + { 3375 + struct device_node *node = phydev->mdio.dev.of_node; 3376 + struct device_node *mdi; 3377 + struct phy_port *port; 3378 + int err; 3379 + 3380 + if (!IS_ENABLED(CONFIG_OF_MDIO)) 3381 + return 0; 3382 + 3383 + if (!node) 3384 + return 0; 3385 + 3386 + mdi = of_get_child_by_name(node, "mdi"); 3387 + if (!mdi) 3388 + return 0; 3389 + 3390 + for_each_available_child_of_node_scoped(mdi, port_node) { 3391 + port = phy_of_parse_port(port_node); 3392 + if (IS_ERR(port)) { 3393 + err = PTR_ERR(port); 3394 + goto out_err; 3395 + } 3396 + 3397 + port->parent_type = PHY_PORT_PHY; 3398 + port->phy = phydev; 3399 + err = phy_add_port(phydev, port); 3400 + if (err) { 3401 + phy_port_destroy(port); 3402 + goto out_err; 3403 + } 3404 + } 3405 + of_node_put(mdi); 3406 + 3407 + return 0; 3408 + 3409 + out_err: 3410 + phy_cleanup_ports(phydev); 3411 + of_node_put(mdi); 3412 + return err; 3413 + } 3414 + 3415 + static int phy_setup_ports(struct phy_device *phydev) 3416 + { 3417 + __ETHTOOL_DECLARE_LINK_MODE_MASK(ports_supported); 3418 + struct phy_port *port; 3419 + int ret; 3420 + 3421 + ret = of_phy_ports(phydev); 3422 + if (ret) 3423 + return ret; 3424 + 3425 + if (phydev->n_ports < phydev->max_n_ports) { 3426 + ret = phy_default_setup_single_port(phydev); 3427 + if (ret) 3428 + goto out; 3429 + } 3430 + 3431 + linkmode_zero(ports_supported); 3432 + 3433 + /* Aggregate the supported modes, which are made-up of : 3434 + * - What the PHY itself supports 3435 + * - What the sum of all ports support 3436 + */ 3437 + list_for_each_entry(port, &phydev->ports, head) 3438 + if (port->active) 3439 + linkmode_or(ports_supported, ports_supported, 3440 + port->supported); 3441 + 3442 + if (!linkmode_empty(ports_supported)) 3443 + linkmode_and(phydev->supported, phydev->supported, 3444 + ports_supported); 3445 + 3446 + /* For now, the phy->port field is set as the first active port's type */ 3447 + list_for_each_entry(port, &phydev->ports, head) 3448 + if (port->active) { 3449 + phydev->port = phy_port_get_type(port); 3450 + break; 3451 + } 3452 + 3453 + return 0; 3454 + 3455 + out: 3456 + phy_cleanup_ports(phydev); 3457 + return ret; 3458 + } 3459 + 3381 3460 /** 3382 3461 * fwnode_mdio_find_device - Given a fwnode, find the mdio_device 3383 3462 * @fwnode: pointer to the mdio_device's fwnode ··· 3647 3462 phydev->is_gigabit_capable = 1; 3648 3463 3649 3464 of_set_phy_supported(phydev); 3465 + 3466 + err = phy_setup_ports(phydev); 3467 + if (err) 3468 + goto out; 3469 + 3650 3470 phy_advertise_supported(phydev); 3651 3471 3652 3472 /* Get PHY default EEE advertising modes and handle them as potentially ··· 3726 3536 phy_leds_unregister(phydev); 3727 3537 3728 3538 phydev->state = PHY_DOWN; 3539 + 3540 + phy_cleanup_ports(phydev); 3729 3541 3730 3542 sfp_bus_del_upstream(phydev->sfp_bus); 3731 3543 phydev->sfp_bus = NULL;
+153
drivers/net/phy/phy_port.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* Framework to drive Ethernet ports 3 + * 4 + * Copyright (c) 2024 Maxime Chevallier <maxime.chevallier@bootlin.com> 5 + */ 6 + 7 + #include <linux/linkmode.h> 8 + #include <linux/of.h> 9 + #include <linux/phy_port.h> 10 + 11 + #include "phy-caps.h" 12 + 13 + /** 14 + * phy_port_alloc() - Allocate a new phy_port 15 + * 16 + * Returns: a newly allocated struct phy_port, or NULL. 17 + */ 18 + struct phy_port *phy_port_alloc(void) 19 + { 20 + struct phy_port *port; 21 + 22 + port = kzalloc(sizeof(*port), GFP_KERNEL); 23 + if (!port) 24 + return NULL; 25 + 26 + linkmode_zero(port->supported); 27 + INIT_LIST_HEAD(&port->head); 28 + 29 + return port; 30 + } 31 + EXPORT_SYMBOL_GPL(phy_port_alloc); 32 + 33 + /** 34 + * phy_port_destroy() - Free a struct phy_port 35 + * @port: The port to destroy 36 + */ 37 + void phy_port_destroy(struct phy_port *port) 38 + { 39 + kfree(port); 40 + } 41 + EXPORT_SYMBOL_GPL(phy_port_destroy); 42 + 43 + /** 44 + * phy_of_parse_port() - Create a phy_port from a firmware representation 45 + * @dn: device_node representation of the port, following the 46 + * ethernet-connector.yaml binding 47 + * 48 + * Returns: a newly allocated and initialized phy_port pointer, or an ERR_PTR. 49 + */ 50 + struct phy_port *phy_of_parse_port(struct device_node *dn) 51 + { 52 + struct fwnode_handle *fwnode = of_fwnode_handle(dn); 53 + enum ethtool_link_medium medium; 54 + struct phy_port *port; 55 + const char *med_str; 56 + u32 pairs = 0, mediums = 0; 57 + int ret; 58 + 59 + ret = fwnode_property_read_string(fwnode, "media", &med_str); 60 + if (ret) 61 + return ERR_PTR(ret); 62 + 63 + medium = ethtool_str_to_medium(med_str); 64 + if (medium == ETHTOOL_LINK_MEDIUM_NONE) 65 + return ERR_PTR(-EINVAL); 66 + 67 + if (medium == ETHTOOL_LINK_MEDIUM_BASET) { 68 + ret = fwnode_property_read_u32(fwnode, "pairs", &pairs); 69 + if (ret) 70 + return ERR_PTR(ret); 71 + 72 + switch (pairs) { 73 + case 1: /* BaseT1 */ 74 + case 2: /* 100BaseTX */ 75 + case 4: 76 + break; 77 + default: 78 + pr_err("%u is not a valid number of pairs\n", pairs); 79 + return ERR_PTR(-EINVAL); 80 + } 81 + } 82 + 83 + if (pairs && medium != ETHTOOL_LINK_MEDIUM_BASET) { 84 + pr_err("pairs property is only compatible with BaseT medium\n"); 85 + return ERR_PTR(-EINVAL); 86 + } 87 + 88 + mediums |= BIT(medium); 89 + 90 + if (!mediums) 91 + return ERR_PTR(-EINVAL); 92 + 93 + port = phy_port_alloc(); 94 + if (!port) 95 + return ERR_PTR(-ENOMEM); 96 + 97 + port->pairs = pairs; 98 + port->mediums = mediums; 99 + 100 + return port; 101 + } 102 + EXPORT_SYMBOL_GPL(phy_of_parse_port); 103 + 104 + /** 105 + * phy_port_update_supported() - Setup the port->supported field 106 + * @port: the port to update 107 + * 108 + * Once the port's medium list and number of pairs has been configured based 109 + * on firmware, straps and vendor-specific properties, this function may be 110 + * called to update the port's supported linkmodes list. 111 + * 112 + * Any mode that was manually set in the port's supported list remains set. 113 + */ 114 + void phy_port_update_supported(struct phy_port *port) 115 + { 116 + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0 }; 117 + unsigned long mode; 118 + int i; 119 + 120 + for_each_set_bit(i, &port->mediums, __ETHTOOL_LINK_MEDIUM_LAST) { 121 + linkmode_zero(supported); 122 + phy_caps_medium_get_supported(supported, i, port->pairs); 123 + linkmode_or(port->supported, port->supported, supported); 124 + } 125 + 126 + /* If there's no pairs specified, we grab the default number of 127 + * pairs as the max of the default pairs for each linkmode 128 + */ 129 + if (!port->pairs) 130 + for_each_set_bit(mode, port->supported, 131 + __ETHTOOL_LINK_MODE_MASK_NBITS) 132 + port->pairs = max_t(int, port->pairs, 133 + ethtool_linkmode_n_pairs(mode)); 134 + } 135 + EXPORT_SYMBOL_GPL(phy_port_update_supported); 136 + 137 + /** 138 + * phy_port_get_type() - get the PORT_* attribute for that port. 139 + * @port: The port we want the information from 140 + * 141 + * Returns: A PORT_XXX value. 142 + */ 143 + int phy_port_get_type(struct phy_port *port) 144 + { 145 + if (port->mediums & BIT(ETHTOOL_LINK_MEDIUM_BASET)) 146 + return PORT_TP; 147 + 148 + if (phy_port_is_fiber(port)) 149 + return PORT_FIBRE; 150 + 151 + return PORT_OTHER; 152 + } 153 + EXPORT_SYMBOL_GPL(phy_port_get_type);
+11
include/linux/ethtool.h
··· 242 242 __ETHTOOL_LINK_MEDIUM_LAST, 243 243 }; 244 244 245 + #define ETHTOOL_MEDIUM_FIBER_BITS (BIT(ETHTOOL_LINK_MEDIUM_BASES) | \ 246 + BIT(ETHTOOL_LINK_MEDIUM_BASEL) | \ 247 + BIT(ETHTOOL_LINK_MEDIUM_BASEF)) 248 + 249 + enum ethtool_link_medium ethtool_str_to_medium(const char *str); 250 + 251 + static inline int ethtool_linkmode_n_pairs(unsigned int mode) 252 + { 253 + return link_mode_params[mode].pairs; 254 + } 255 + 245 256 /* declare a link mode bitmap */ 246 257 #define __ETHTOOL_DECLARE_LINK_MODE_MASK(name) \ 247 258 DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS)
+55
include/linux/phy.h
··· 327 327 struct device; 328 328 struct kernel_hwtstamp_config; 329 329 struct phylink; 330 + struct phy_port; 330 331 struct sfp_bus; 331 332 struct sfp_upstream_ops; 332 333 struct sk_buff; ··· 646 645 * @master_slave_state: Current master/slave configuration 647 646 * @mii_ts: Pointer to time stamper callbacks 648 647 * @psec: Pointer to Power Sourcing Equipment control struct 648 + * @ports: List of PHY ports structures 649 + * @n_ports: Number of ports currently attached to the PHY 650 + * @max_n_ports: Max number of ports this PHY can expose 649 651 * @lock: Mutex for serialization access to PHY 650 652 * @state_queue: Work queue for state machine 651 653 * @link_down_events: Number of times link was lost ··· 787 783 struct mii_timestamper *mii_ts; 788 784 struct pse_control *psec; 789 785 786 + struct list_head ports; 787 + int n_ports; 788 + int max_n_ports; 789 + 790 790 u8 mdix; 791 791 u8 mdix_ctrl; 792 792 ··· 814 806 #define PHY_F_RXC_ALWAYS_ON 0x40000000 815 807 816 808 #define to_phy_device(__dev) container_of_const(to_mdio_device(__dev), struct phy_device, mdio) 809 + 810 + #define phy_for_each_port(phydev, port) \ 811 + list_for_each_entry(port, &(phydev)->ports, head) 817 812 818 813 /** 819 814 * struct phy_tdr_config - Configuration of a TDR raw test ··· 1518 1507 * Returns the time in jiffies until the next update event. 1519 1508 */ 1520 1509 unsigned int (*get_next_update_time)(struct phy_device *dev); 1510 + 1511 + /** 1512 + * @attach_mii_port: Attach the given MII port to the PHY device 1513 + * @dev: PHY device to notify 1514 + * @port: The port being added 1515 + * 1516 + * Called when an MII port that needs to be driven by the PHY is found. 1517 + * 1518 + * The port that is being passed may or may not be initialized. If it is 1519 + * already initialized, it is by the generic port representation from 1520 + * devicetree, which superseeds any strapping or vendor-specific 1521 + * properties. 1522 + * 1523 + * If the port isn't initialized, the port->mediums and port->lanes 1524 + * fields must be set, possibly according to strapping information. 1525 + * 1526 + * The PHY driver must set the port->interfaces field to indicate the 1527 + * possible MII modes that this PHY can output on the port. 1528 + * 1529 + * Returns 0, or an error code. 1530 + */ 1531 + int (*attach_mii_port)(struct phy_device *dev, struct phy_port *port); 1532 + 1533 + /** 1534 + * @attach_mdi_port: Attach the given MII port to the PHY device 1535 + * @dev: PHY device to notify 1536 + * @port: The port being added 1537 + * 1538 + * Called when a port that needs to be driven by the PHY is found. The 1539 + * number of time this will be called depends on phydev->max_n_ports, 1540 + * which the driver can change in .probe(). 1541 + * 1542 + * The port that is being passed may or may not be initialized. If it is 1543 + * already initialized, it is by the generic port representation from 1544 + * devicetree, which superseeds any strapping or vendor-specific 1545 + * properties. 1546 + * 1547 + * If the port isn't initialized, the port->mediums and port->lanes 1548 + * fields must be set, possibly according to strapping information. 1549 + * 1550 + * Returns 0, or an error code. 1551 + */ 1552 + int (*attach_mdi_port)(struct phy_device *dev, struct phy_port *port); 1521 1553 }; 1522 1554 #define to_phy_driver(d) container_of_const(to_mdio_common_driver(d), \ 1523 1555 struct phy_driver, mdiodrv) ··· 2364 2310 void phy_mac_interrupt(struct phy_device *phydev); 2365 2311 void phy_start_machine(struct phy_device *phydev); 2366 2312 void phy_stop_machine(struct phy_device *phydev); 2313 + 2367 2314 void phy_ethtool_ksettings_get(struct phy_device *phydev, 2368 2315 struct ethtool_link_ksettings *cmd); 2369 2316 int phy_ethtool_ksettings_set(struct phy_device *phydev,
+96
include/linux/phy_port.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef __PHY_PORT_H 4 + #define __PHY_PORT_H 5 + 6 + #include <linux/ethtool.h> 7 + #include <linux/types.h> 8 + #include <linux/phy.h> 9 + 10 + struct phy_port; 11 + 12 + /** 13 + * enum phy_port_parent - The device this port is attached to 14 + * 15 + * @PHY_PORT_PHY: Indicates that the port is driven by a PHY device 16 + */ 17 + enum phy_port_parent { 18 + PHY_PORT_PHY, 19 + }; 20 + 21 + struct phy_port_ops { 22 + /* Sometimes, the link state can be retrieved from physical, 23 + * out-of-band channels such as the LOS signal on SFP. These 24 + * callbacks allows notifying the port about state changes 25 + */ 26 + void (*link_up)(struct phy_port *port); 27 + void (*link_down)(struct phy_port *port); 28 + 29 + /* If the port acts as a Media Independent Interface (Serdes port), 30 + * configures the port with the relevant state and mode. When enable is 31 + * not set, interface should be ignored 32 + */ 33 + int (*configure_mii)(struct phy_port *port, bool enable, phy_interface_t interface); 34 + }; 35 + 36 + /** 37 + * struct phy_port - A representation of a network device physical interface 38 + * 39 + * @head: Used by the port's parent to list ports 40 + * @parent_type: The type of device this port is directly connected to 41 + * @phy: If the parent is PHY_PORT_PHYDEV, the PHY controlling that port 42 + * @ops: Callback ops implemented by the port controller 43 + * @pairs: The number of pairs this port has, 0 if not applicable 44 + * @mediums: Bitmask of the physical mediums this port provides access to 45 + * @supported: The link modes this port can expose, if this port is MDI (not MII) 46 + * @interfaces: The MII interfaces this port supports, if this port is MII 47 + * @not_described: Indicates to the parent driver if this port isn't described, 48 + * so it's up to the parent to filter its capabilities. 49 + * @active: Indicates if the port is currently part of the active link. 50 + * @is_mii: Indicates if this port is MII (Media Independent Interface), 51 + * or MDI (Media Dependent Interface). 52 + */ 53 + struct phy_port { 54 + struct list_head head; 55 + enum phy_port_parent parent_type; 56 + union { 57 + struct phy_device *phy; 58 + }; 59 + 60 + const struct phy_port_ops *ops; 61 + 62 + int pairs; 63 + unsigned long mediums; 64 + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); 65 + DECLARE_PHY_INTERFACE_MASK(interfaces); 66 + 67 + unsigned int not_described:1; 68 + unsigned int active:1; 69 + unsigned int is_mii:1; 70 + }; 71 + 72 + struct phy_port *phy_port_alloc(void); 73 + void phy_port_destroy(struct phy_port *port); 74 + 75 + static inline struct phy_device *port_phydev(struct phy_port *port) 76 + { 77 + return port->phy; 78 + } 79 + 80 + struct phy_port *phy_of_parse_port(struct device_node *dn); 81 + 82 + static inline bool phy_port_is_copper(struct phy_port *port) 83 + { 84 + return port->mediums == BIT(ETHTOOL_LINK_MEDIUM_BASET); 85 + } 86 + 87 + static inline bool phy_port_is_fiber(struct phy_port *port) 88 + { 89 + return !!(port->mediums & ETHTOOL_MEDIUM_FIBER_BITS); 90 + } 91 + 92 + void phy_port_update_supported(struct phy_port *port); 93 + 94 + int phy_port_get_type(struct phy_port *port); 95 + 96 + #endif
+27
net/ethtool/common.c
··· 468 468 static_assert(ARRAY_SIZE(link_mode_params) == __ETHTOOL_LINK_MODE_MASK_NBITS); 469 469 EXPORT_SYMBOL_GPL(link_mode_params); 470 470 471 + static const char ethtool_link_medium_names[][ETH_GSTRING_LEN] = { 472 + [ETHTOOL_LINK_MEDIUM_BASET] = "BaseT", 473 + [ETHTOOL_LINK_MEDIUM_BASEK] = "BaseK", 474 + [ETHTOOL_LINK_MEDIUM_BASES] = "BaseS", 475 + [ETHTOOL_LINK_MEDIUM_BASEC] = "BaseC", 476 + [ETHTOOL_LINK_MEDIUM_BASEL] = "BaseL", 477 + [ETHTOOL_LINK_MEDIUM_BASED] = "BaseD", 478 + [ETHTOOL_LINK_MEDIUM_BASEE] = "BaseE", 479 + [ETHTOOL_LINK_MEDIUM_BASEF] = "BaseF", 480 + [ETHTOOL_LINK_MEDIUM_BASEV] = "BaseV", 481 + [ETHTOOL_LINK_MEDIUM_BASEMLD] = "BaseMLD", 482 + [ETHTOOL_LINK_MEDIUM_NONE] = "None", 483 + }; 484 + static_assert(ARRAY_SIZE(ethtool_link_medium_names) == __ETHTOOL_LINK_MEDIUM_LAST); 485 + 471 486 const char netif_msg_class_names[][ETH_GSTRING_LEN] = { 472 487 [NETIF_MSG_DRV_BIT] = "drv", 473 488 [NETIF_MSG_PROBE_BIT] = "probe", ··· 1216 1201 ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_DELETE_NTF, context_id); 1217 1202 } 1218 1203 EXPORT_SYMBOL(ethtool_rxfh_context_lost); 1204 + 1205 + enum ethtool_link_medium ethtool_str_to_medium(const char *str) 1206 + { 1207 + int i; 1208 + 1209 + for (i = 0; i < __ETHTOOL_LINK_MEDIUM_LAST; i++) 1210 + if (!strcmp(ethtool_link_medium_names[i], str)) 1211 + return i; 1212 + 1213 + return ETHTOOL_LINK_MEDIUM_NONE; 1214 + } 1215 + EXPORT_SYMBOL_GPL(ethtool_str_to_medium);