STRUCT PHY_DRIVER(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

🌸 struct phy_driver(9) β€” Kernel API Manual 🌸

✨ NAME ✨

struct phy_driver β€” the driver structure for a particular PHY type. Bestie, this is the blueprint a PHY driver fills out so the kernel knows what it can do πŸ’–.

πŸ¦‹ SYNOPSIS πŸ¦‹

       struct phy_driver {
           struct mdio_driver_common mdiodrv;
           u32 phy_id;
           char *name;
           u32 phy_id_mask;
           const unsigned long * const features;
           u32 flags;
           const void *driver_data;
           int (*soft_reset)(struct phy_device *phydev);
           int (*config_init)(struct phy_device *phydev);
           int (*probe)(struct phy_device *phydev);
           int (*get_features)(struct phy_device *phydev);
           unsigned int (*inband_caps)(struct phy_device *phydev,
       phy_interface_t interface);
           int (*config_inband)(struct phy_device *phydev, unsigned int modes);
           int (*get_rate_matching)(struct phy_device *phydev, phy_interface_t
       iface);
           int (*suspend)(struct phy_device *phydev);
           int (*resume)(struct phy_device *phydev);
           int (*config_aneg)(struct phy_device *phydev);
           int (*aneg_done)(struct phy_device *phydev);
           int (*read_status)(struct phy_device *phydev);
           int (*config_intr)(struct phy_device *phydev);
           irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
           void (*remove)(struct phy_device *phydev);
           int (*match_phy_device)(struct phy_device *phydev, const struct
       phy_driver *phydrv);
           int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
           void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo
       *wol);
           void (*link_change_notify)(struct phy_device *dev);
           int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum);
           int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum, u16
       val);
           int (*read_page)(struct phy_device *dev);
           int (*write_page)(struct phy_device *dev, int page);
           int (*module_info)(struct phy_device *dev, struct ethtool_modinfo
       *modinfo);
           int (*module_eeprom)(struct phy_device *dev, struct ethtool_eeprom
       *ee, u8 *data);
           int (*cable_test_start)(struct phy_device *dev);
           int (*cable_test_tdr_start)(struct phy_device *dev, const struct
       phy_tdr_config *config);
           int (*cable_test_get_status)(struct phy_device *dev, bool
       *finished);
           void (*get_phy_stats)(struct phy_device *dev, struct
       ethtool_eth_phy_stats *eth_stats, struct ethtool_phy_stats *stats);
           void (*get_link_stats)(struct phy_device *dev, struct
       ethtool_link_ext_stats *link_stats);
           int (*update_stats)(struct phy_device *dev);
           int (*get_sset_count)(struct phy_device *dev);
           void (*get_strings)(struct phy_device *dev, u8 *data);
           void (*get_stats)(struct phy_device *dev, struct ethtool_stats
       *stats, u64 *data);
           int (*disable_autonomous_eee)(struct phy_device *dev);
           int (*get_tunable)(struct phy_device *dev, struct ethtool_tunable
       *tuna, void *data);
           int (*set_tunable)(struct phy_device *dev, struct ethtool_tunable
       *tuna, const void *data);
           int (*set_loopback)(struct phy_device *dev, bool enable, int speed);
           int (*get_sqi)(struct phy_device *dev);
           int (*get_sqi_max)(struct phy_device *dev);
           int (*get_mse_capability)(struct phy_device *dev, struct
       phy_mse_capability *cap);
           int (*get_mse_snapshot)(struct phy_device *dev, enum phy_mse_channel
       channel, struct phy_mse_snapshot *snapshot);
           int (*get_plca_cfg)(struct phy_device *dev, struct phy_plca_cfg
       *plca_cfg);
           int (*set_plca_cfg)(struct phy_device *dev, const struct
       phy_plca_cfg *plca_cfg);
           int (*get_plca_status)(struct phy_device *dev, struct
       phy_plca_status *plca_st);
           int (*led_brightness_set)(struct phy_device *dev, u8 index, enum
       led_brightness value);
           int (*led_blink_set)(struct phy_device *dev, u8 index, unsigned long
       *delay_on, unsigned long *delay_off);
           int (*led_hw_is_supported)(struct phy_device *dev, u8 index,
       unsigned long rules);
           int (*led_hw_control_set)(struct phy_device *dev, u8 index, unsigned
       long rules);
           int (*led_hw_control_get)(struct phy_device *dev, u8 index, unsigned
       long *rules);
           int (*led_polarity_set)(struct phy_device *dev, int index, unsigned
       long modes);
           unsigned int (*get_next_update_time)(struct phy_device *dev);
           int (*attach_mii_port)(struct phy_device *dev, struct phy_port
       *port);
           int (*attach_mdi_port)(struct phy_device *dev, struct phy_port
       *port);
        };

πŸ’… Members πŸ’…

okay so here’s the cast of characters in this struct β€” each one is a little hook the PHY driver can provide πŸŽ€

  • mdiodrv β€” data common to all MDIO devices.
  • phy_id β€” the result of reading the UID registers of this PHY type, and ANDing them with the phy_id_mask. This driver only works for PHYs with IDs which match this field.
  • name β€” the friendly name of this PHY type.
  • phy_id_mask β€” defines the important bits of the phy_id.
  • features β€” a mandatory list of features (speed, duplex, etc) supported by this PHY.
  • flags β€” a bitfield defining certain other features this PHY supports (like interrupts).
  • driver_data β€” static driver data.
  • soft_reset β€” called to issue a PHY software reset.
  • config_init β€” called to initialize the PHY, including after a reset.
  • probe β€” called during discovery. Used to set up device-specific structures, if any.
  • get_features β€” probe the hardware to determine what abilities it has. Should only set phydev->supported.
  • inband_caps β€” query whether in-band is supported for the given PHY interface mode. Returns a bitmask of bits defined by enum link_inband_signalling.
  • config_inband β€” configure in-band mode for the PHY.
  • get_rate_matching β€” get the supported type of rate matching for a particular phy interface. This is used by phy consumers to determine whether to advertise lower-speed modes for that interface. It is assumed that if a rate matching mode is supported on an interface, then that interface’s rate can be adapted to all slower link speeds supported by the phy. If the interface is not supported, this should return RATE_MATCH_NONE.
  • suspend β€” suspend the hardware, saving state if needed.
  • resume β€” resume the hardware, restoring state if needed.
  • config_aneg β€” configures the advertisement and resets autonegotiation if phydev->autoneg is on, forces the speed to the current settings in phydev if phydev->autoneg is off.
  • aneg_done β€” determines the auto negotiation result.
  • read_status β€” determines the negotiated speed and duplex.
  • config_intr β€” enables or disables interrupts. It should also clear any pending interrupts prior to enabling the IRQs and after disabling them.
  • handle_interrupt β€” override default interrupt handling.
  • remove β€” clears up any memory if needed.
  • match_phy_device β€” returns true if this is a suitable driver for the given phydev. If NULL, matching is based on phy_id and phy_id_mask.
  • set_wol β€” some devices (e.g. qnap TS-119P II) require PHY register changes to enable Wake on LAN, so set_wol is provided to be called in the ethernet driver’s set_wol function.
  • get_wol β€” see set_wol, but for checking whether Wake on LAN is enabled.
  • link_change_notify β€” called to inform a PHY device driver when the core is about to change the link state. This callback is supposed to be used as fixup hook for drivers that need to take action when the link state changes. Drivers are by no means allowed to mess with the PHY device structure in their implementations.
  • read_mmd β€” PHY specific driver override for reading a MMD register. This function is optional for PHY specific drivers. When not provided, the default MMD read function will be used by phy_read_mmd, which will use either a direct read for Clause 45 PHYs or an indirect read for Clause 22 PHYs. devnum is the MMD device number within the PHY device, regnum is the register within the selected MMD device.
  • write_mmd β€” PHY specific driver override for writing a MMD register. This function is optional for PHY specific drivers. When not provided, the default MMD write function will be used by phy_write_mmd, which will use either a direct write for Clause 45 PHYs, or an indirect write for Clause 22 PHYs. devnum is the MMD device number within the PHY device, regnum is the register within the selected MMD device. val is the value to be written.
  • read_page β€” return the current PHY register page number.
  • write_page β€” set the current PHY register page number.
  • module_info β€” get the size and type of the eeprom contained within a plug-in module.
  • module_eeprom β€” get the eeprom information from the plug-in module.
  • cable_test_start β€” start a cable test.
  • cable_test_tdr_start β€” start a raw TDR cable test.
  • cable_test_get_status β€” once per second, or on interrupt, request the status of the test.
  • get_phy_stats β€” retrieve PHY statistics. dev: the PHY device for which the statistics are retrieved. eth_stats: structure where Ethernet PHY stats will be stored. stats: structure where additional PHY-specific stats will be stored.

Retrieves the supported PHY statistics and populates the provided structures. The input structures are pre-initialized with ETHTOOL_STAT_NOT_SET, and the driver must only modify members corresponding to supported statistics. Unmodified members will remain set to ETHTOOL_STAT_NOT_SET and will not be returned to userspace.

  • get_link_stats β€” retrieve link statistics. dev: the PHY device for which the statistics are retrieved. link_stats: structure where link-specific stats will be stored.

Retrieves link-related statistics for the given PHY device. The input structure is pre-initialized with ETHTOOL_STAT_NOT_SET, and the driver must only modify members corresponding to supported statistics. Unmodified members will remain set to ETHTOOL_STAT_NOT_SET and will not be returned to userspace.

  • update_stats β€” trigger periodic statistics updates. dev: the PHY device for which statistics updates are triggered.
  • update_stats β€” pulls the stats off the PHY on a schedule so your local 64-bit counters stay accurate ✨ this matters because some PHYs only give you reduced-width counters (like 16-bit or 32-bit) and those overflow way faster than 64-bit ones. By calling this callback, drivers can grab the current counter values, catch the overflow, and accumulate everything into local 64-bit counters so get_phy_stats and get_link_stats report the truth. πŸ’–

Return: 0 on success or a negative error code on failure.

  • get_sset_count β€” number of statistic counters πŸŽ€
  • get_strings β€” the names of the statistic counters πŸ’…
  • get_stats β€” hands back the statistic counter values πŸ‘‘
  • disable_autonomous_eee β€” turns off PHY-autonomous EEE 🌸

So like, some PHYs just manage EEE all on their own and won't let the MAC control LPI signaling. This callback disables that autonomous EEE right at the PHY.

Return: 0 on success, negative errno on failure.

  • get_tunable β€” returns the value of a tunable πŸ¦‹
  • set_tunable β€” sets the value of a tunable πŸ¦‹
  • set_loopback β€” sets the loopback mode of the PHY. enable selects whether the loopback mode is enabled or disabled. If loopback is enabled, then the speed of the loopback mode can be requested with the speed argument. If speed is zero, then any speed can be selected. If speed is > 0, then that speed shall be selected for the loopback mode β€” or EOPNOTSUPP shall be returned if speed selection isn't supported. 😀
  • get_sqi β€” get the signal quality indication ✨
  • get_sqi_max β€” get the maximum signal quality indication 🌈
  • get_mse_capability β€” get capabilities and scale of MSE measurement. dev: PHY device. cap: Output (filled on success).

Fills cap with the PHY's MSE capability for the current link mode: scale limits (max_average_mse, max_peak_mse), update interval (refresh_rate_ps), sample length (num_symbols) and the capability bitmask (supported_caps). 🧚

Implementations may defer the capability report until the hardware has converged β€” in that case they should return -EAGAIN and let the caller retry later. No biggie, just try again bestie πŸ’œ

Return: 0 on success. On failure, returns a negative errno code, such as -EOPNOTSUPP if MSE measurement isn't supported by the PHY or in the current link mode, or -EAGAIN if the capability information isn't available yet.

  • get_mse_snapshot β€” retrieve a snapshot of MSE diagnostic values. dev: PHY device. channel: Channel identifier (PHY_MSE_CHANNEL_*). snapshot: Output (filled on success).

Fills snapshot with a correlated set of MSE values from the most recent measurement window. πŸͺŸ

Callers must validate channel against supported_caps returned by get_mse_capability. Drivers must not coerce channel β€” if the requested selector isn't implemented by the device or the current link mode, the operation must fail. periodt. πŸ’€

worst_peak_mse is latched and must be treated as read-to-clear.

Return: 0 on success. On failure, returns a negative errno code, such as -EOPNOTSUPP if MSE measurement isn't supported by the PHY or in the current link mode, or -EAGAIN if measurements aren't available yet.

  • get_plca_cfg β€” return the current PLCA configuration πŸŽ€
  • set_plca_cfg β€” set the PLCA configuration πŸŽ€
  • get_plca_status β€” return the current PLCA status info πŸŽ€
  • led_brightness_set β€” set a PHY LED brightness. index indicates which of the PHY's LEDs should be set. value follows the standard LED class meaning, e.g. LED_OFF, LED_HALF, LED_FULL. πŸ’‘
  • led_blink_set β€” set a PHY LED blinking. index indicates which of the PHY's LEDs should be configured to blink. Delays are in milliseconds, and if both are zero then a sensible default should be chosen. The call should adjust the timings in that case β€” and if it can't match the values specified exactly. ✨
  • led_hw_is_supported β€” can the HW support the given rules? dev: PHY device which has the LED. index: which LED of the PHY device. rules: the rules the core is interested in.

Return 0 if yes, -EOPNOTSUPP if not, or an error code. 🫢

  • led_hw_control_set β€” set the HW to control the LED. dev: PHY device which has the LED. index: which LED of the PHY device. rules: the rules used to control the LED.

Returns 0, or an error code.

  • led_hw_control_get β€” get how the HW is controlling the LED. dev: PHY device which has the LED. index: which LED of the PHY device. rules: pointer to the rules used to control the LED.

Set *rules to how the HW is currently blinking. Returns 0 on success, or an error code if the current blinking can't be represented in rules, or some other error happens. 😭

  • led_polarity_set β€” set the LED polarity modes. dev: PHY device which has the LED. index: which LED of the PHY device. modes: bitmap of LED polarity modes.

Configure the LED with all the required polarity modes in modes so it correctly turns ON or OFF. πŸ”₯

Returns 0, or an error code.

  • get_next_update_time β€” get the time until the next update event. dev: PHY device.

Callback to determine the time (in jiffies) until the next update event for the PHY state machine. Lets PHY drivers dynamically adjust polling intervals based on link state or other conditions. ⏰

Returns the time in jiffies until the next update event.

  • attach_mii_port β€” attach the given MII port to the PHY device. dev: PHY device to notify. port: the port being added.

Called when an MII port that needs to be driven by the PHY is found. πŸ”

The port being passed may or may not be initialized. If it is already initialized, it's by the generic port representation from devicetree β€” which supersedes any strapping or vendor-specific properties. no cap.

If the port isn't initialized, the port->mediums and port->lanes fields must be set, possibly according to strapping information.

The PHY driver must set the port->interfaces field to indicate the possible MII modes that this PHY can output on the port.

Returns 0, or an error code. πŸ’œ

  • attach_mdi_port β€” attach the given MII port to the PHY device. dev: PHY device to notify. port: the port being added.

Called when a port that needs to be driven by the PHY is found. The number of times this will be called depends on phydev->max_n_ports, which the driver can change in .probe. 🩺

The port being passed may or may not be initialized. If it is already initialized, it's by the generic port representation from devicetree β€” which supersedes any strapping or vendor-specific properties.

If the port isn't initialized, the port->mediums and port->lanes fields must be set, possibly according to strapping information.

Returns 0, or an error code. 🫢


✨ DESCRIPTION ✨

Okay so the vibes here: all of these functions are optional πŸ’… If config_aneg or read_status aren't implemented, the phy core just uses the genphy versions. Easy.

⚠️ Heads up bestie β€” none of these functions should be called from interrupt time. 🚨

The goal is for the bus read/write functions to be able to block when the bus transaction is happening, and be freed up by an interrupt. (The MPC85xx has this ability, though it is not currently supported in the driver.) πŸ¦‹


πŸŽ€ SEE ALSO πŸŽ€

Kernel file ./include/linux/phy.h: enum phy_interface_t(9), phy_modes(9), rgmii_clock(9), struct mdio_bus_stats(9), struct mii_bus(9), mdiobus_alloc(9), enum phy_state(9), struct phy_c45_device_ids(9), struct phy_oatc14_sqi_capability(9), struct phy_device(9), struct phy_tdr_config(9), enum link_inband_signalling(9), struct phy_plca_cfg(9), struct phy_plca_status(9), struct phy_led(9), struct phy_mse_capability(9), struct phy_mse_snapshot(9), phy_id_compare(9), phy_id_compare_vendor(9), phy_id_compare_model(9), phydev_id_compare(9), phy_is_started(9), phy_driver_is_genphy(9), phy_disable_eee_mode(9), phy_can_wakeup(9), phy_may_wakeup(9), phy_read(9), __phy_read(9), phy_write(9), __phy_write(9), __phy_modify_changed(9), phy_read_mmd_poll_timeout(9), __phy_set_bits(9), __phy_clear_bits(9), phy_set_bits(9), phy_clear_bits(9), __phy_set_bits_mmd(9), __phy_clear_bits_mmd(9), phy_set_bits_mmd(9), phy_clear_bits_mmd(9), phy_interrupt_is_valid(9), phy_polling_mode(9), phy_has_hwtstamp(9), phy_has_rxtstamp(9), phy_has_tsinfo(9), phy_has_txtstamp(9), phy_is_default_hwtstamp(9), phy_on_sfp(9), phy_interface_mode_is_rgmii(9), phy_interface_mode_is_8023z(9), phy_interface_is_rgmii(9), phy_is_pseudo_fixed_link(9), phy_module_driver(9) 🌈


That's a wrap, bestie! πŸ’– This lil masterpiece lives in ./include/linux, and the version you're reading is the October 2026 drop of the struct phy_driver(9) page. Go forth and slay your PHY driver, you absolute icon πŸ‘‘βœ¨

πŸ“¦ where this page comes from