// SPDX-License-Identifier: GPL-2.0+ /* Framework for configuring and reading PHY devices * Based on code in sungem_phy.c and gianfar_phy.c * * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. * Copyright (c) 2006, 2007 Maciej W. Rozycki */ #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/unistd.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/netlink.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/ethtool_netlink.h> #include <linux/phy.h> #include <linux/phy_led_triggers.h> #include <linux/sfp.h> #include <linux/workqueue.h> #include <linux/mdio.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/atomic.h> #include <linux/suspend.h> #include <net/netlink.h> #include <net/genetlink.h> #include <net/sock.h> #define PHY_STATE_TIME … #define PHY_STATE_STR(_state) … \ static const char *phy_state_to_str(enum phy_state st) { … } static void phy_process_state_change(struct phy_device *phydev, enum phy_state old_state) { … } static void phy_link_up(struct phy_device *phydev) { … } static void phy_link_down(struct phy_device *phydev) { … } static const char *phy_pause_str(struct phy_device *phydev) { … } /** * phy_print_status - Convenience function to print out the current phy status * @phydev: the phy_device struct */ void phy_print_status(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_get_rate_matching - determine if rate matching is supported * @phydev: The phy device to return rate matching for * @iface: The interface mode to use * * This determines the type of rate matching (if any) that @phy supports * using @iface. @iface may be %PHY_INTERFACE_MODE_NA to determine if any * interface supports rate matching. * * Return: The type of rate matching @phy supports for @iface, or * %RATE_MATCH_NONE. */ int phy_get_rate_matching(struct phy_device *phydev, phy_interface_t iface) { … } EXPORT_SYMBOL_GPL(…); /** * phy_config_interrupt - configure the PHY device for the requested interrupts * @phydev: the phy_device struct * @interrupts: interrupt flags to configure for this @phydev * * Returns 0 on success or < 0 on error. */ static int phy_config_interrupt(struct phy_device *phydev, bool interrupts) { … } /** * phy_restart_aneg - restart auto-negotiation * @phydev: target phy_device struct * * Restart the autonegotiation on @phydev. Returns >= 0 on success or * negative errno on error. */ int phy_restart_aneg(struct phy_device *phydev) { … } EXPORT_SYMBOL_GPL(…); /** * phy_aneg_done - return auto-negotiation status * @phydev: target phy_device struct * * Description: Return the auto-negotiation status from this @phydev * Returns > 0 on success or < 0 on error. 0 means that auto-negotiation * is still pending. */ int phy_aneg_done(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_find_valid - find a PHY setting that matches the requested parameters * @speed: desired speed * @duplex: desired duplex * @supported: mask of supported link modes * * Locate a supported phy setting that is, in priority order: * - an exact match for the specified speed and duplex mode * - a match for the specified speed, or slower speed * - the slowest supported speed * Returns the matched phy_setting entry, or %NULL if no supported phy * settings were found. */ static const struct phy_setting * phy_find_valid(int speed, int duplex, unsigned long *supported) { … } /** * phy_supported_speeds - return all speeds currently supported by a phy device * @phy: The phy device to return supported speeds of. * @speeds: buffer to store supported speeds in. * @size: size of speeds buffer. * * Description: Returns the number of supported speeds, and fills the speeds * buffer with the supported speeds. If speeds buffer is too small to contain * all currently supported speeds, will return as many speeds as can fit. */ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int *speeds, unsigned int size) { … } /** * phy_check_valid - check if there is a valid PHY setting which matches * speed, duplex, and feature mask * @speed: speed to match * @duplex: duplex to match * @features: A mask of the valid settings * * Description: Returns true if there is a valid setting, false otherwise. */ bool phy_check_valid(int speed, int duplex, unsigned long *features) { … } EXPORT_SYMBOL(…); /** * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex * @phydev: the target phy_device struct * * Description: Make sure the PHY is set to supported speeds and * duplexes. Drop down by one in this order: 1000/FULL, * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. */ static void phy_sanitize_settings(struct phy_device *phydev) { … } void phy_ethtool_ksettings_get(struct phy_device *phydev, struct ethtool_link_ksettings *cmd) { … } EXPORT_SYMBOL(…); /** * phy_mii_ioctl - generic PHY MII ioctl interface * @phydev: the phy_device struct * @ifr: &struct ifreq for socket ioctl's * @cmd: ioctl cmd to execute * * Note that this function is currently incompatible with the * PHYCONTROL layer. It changes registers without regard to * current state. Use at own risk. */ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) { … } EXPORT_SYMBOL(…); /** * phy_do_ioctl - generic ndo_eth_ioctl implementation * @dev: the net_device struct * @ifr: &struct ifreq for socket ioctl's * @cmd: ioctl cmd to execute */ int phy_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { … } EXPORT_SYMBOL(…); /** * phy_do_ioctl_running - generic ndo_eth_ioctl implementation but test first * * @dev: the net_device struct * @ifr: &struct ifreq for socket ioctl's * @cmd: ioctl cmd to execute * * Same as phy_do_ioctl, but ensures that net_device is running before * handling the ioctl. */ int phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd) { … } EXPORT_SYMBOL(…); /** * __phy_hwtstamp_get - Get hardware timestamping configuration from PHY * * @phydev: the PHY device structure * @config: structure holding the timestamping configuration * * Query the PHY device for its current hardware timestamping configuration. */ int __phy_hwtstamp_get(struct phy_device *phydev, struct kernel_hwtstamp_config *config) { … } /** * __phy_hwtstamp_set - Modify PHY hardware timestamping configuration * * @phydev: the PHY device structure * @config: structure holding the timestamping configuration * @extack: netlink extended ack structure, for error reporting */ int __phy_hwtstamp_set(struct phy_device *phydev, struct kernel_hwtstamp_config *config, struct netlink_ext_ack *extack) { … } /** * phy_queue_state_machine - Trigger the state machine to run soon * * @phydev: the phy_device struct * @jiffies: Run the state machine after these jiffies */ void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies) { … } EXPORT_SYMBOL(…); /** * phy_trigger_machine - Trigger the state machine to run now * * @phydev: the phy_device struct */ void phy_trigger_machine(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); static void phy_abort_cable_test(struct phy_device *phydev) { … } /** * phy_ethtool_get_strings - Get the statistic counter names * * @phydev: the phy_device struct * @data: Where to put the strings */ int phy_ethtool_get_strings(struct phy_device *phydev, u8 *data) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_get_sset_count - Get the number of statistic counters * * @phydev: the phy_device struct */ int phy_ethtool_get_sset_count(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_get_stats - Get the statistic counters * * @phydev: the phy_device struct * @stats: What counters to get * @data: Where to store the counters */ int phy_ethtool_get_stats(struct phy_device *phydev, struct ethtool_stats *stats, u64 *data) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_get_plca_cfg - Get PLCA RS configuration * @phydev: the phy_device struct * @plca_cfg: where to store the retrieved configuration * * Retrieve the PLCA configuration from the PHY. Return 0 on success or a * negative value if an error occurred. */ int phy_ethtool_get_plca_cfg(struct phy_device *phydev, struct phy_plca_cfg *plca_cfg) { … } /** * plca_check_valid - Check PLCA configuration before enabling * @phydev: the phy_device struct * @plca_cfg: current PLCA configuration * @extack: extack for reporting useful error messages * * Checks whether the PLCA and PHY configuration are consistent and it is safe * to enable PLCA. Returns 0 on success or a negative value if the PLCA or PHY * configuration is not consistent. */ static int plca_check_valid(struct phy_device *phydev, const struct phy_plca_cfg *plca_cfg, struct netlink_ext_ack *extack) { … } /** * phy_ethtool_set_plca_cfg - Set PLCA RS configuration * @phydev: the phy_device struct * @plca_cfg: new PLCA configuration to apply * @extack: extack for reporting useful error messages * * Sets the PLCA configuration in the PHY. Return 0 on success or a * negative value if an error occurred. */ int phy_ethtool_set_plca_cfg(struct phy_device *phydev, const struct phy_plca_cfg *plca_cfg, struct netlink_ext_ack *extack) { … } /** * phy_ethtool_get_plca_status - Get PLCA RS status information * @phydev: the phy_device struct * @plca_st: where to store the retrieved status information * * Retrieve the PLCA status information from the PHY. Return 0 on success or a * negative value if an error occurred. */ int phy_ethtool_get_plca_status(struct phy_device *phydev, struct phy_plca_status *plca_st) { … } /** * phy_start_cable_test - Start a cable test * * @phydev: the phy_device struct * @extack: extack for reporting useful error messages */ int phy_start_cable_test(struct phy_device *phydev, struct netlink_ext_ack *extack) { … } EXPORT_SYMBOL(…); /** * phy_start_cable_test_tdr - Start a raw TDR cable test * * @phydev: the phy_device struct * @extack: extack for reporting useful error messages * @config: Configuration of the test to run */ int phy_start_cable_test_tdr(struct phy_device *phydev, struct netlink_ext_ack *extack, const struct phy_tdr_config *config) { … } EXPORT_SYMBOL(…); int phy_config_aneg(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_check_link_status - check link status and set state accordingly * @phydev: the phy_device struct * * Description: Check for link and whether autoneg was triggered / is running * and set state accordingly */ static int phy_check_link_status(struct phy_device *phydev) { … } /** * _phy_start_aneg - start auto-negotiation for this PHY device * @phydev: the phy_device struct * * Description: Sanitizes the settings (if we're not autonegotiating * them), and then calls the driver's config_aneg function. * If the PHYCONTROL Layer is operating, we change the state to * reflect the beginning of Auto-negotiation or forcing. */ int _phy_start_aneg(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_start_aneg - start auto-negotiation for this PHY device * @phydev: the phy_device struct * * Description: Sanitizes the settings (if we're not autonegotiating * them), and then calls the driver's config_aneg function. * If the PHYCONTROL Layer is operating, we change the state to * reflect the beginning of Auto-negotiation or forcing. */ int phy_start_aneg(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); static int phy_poll_aneg_done(struct phy_device *phydev) { … } int phy_ethtool_ksettings_set(struct phy_device *phydev, const struct ethtool_link_ksettings *cmd) { … } EXPORT_SYMBOL(…); /** * phy_speed_down - set speed to lowest speed supported by both link partners * @phydev: the phy_device struct * @sync: perform action synchronously * * Description: Typically used to save energy when waiting for a WoL packet * * WARNING: Setting sync to false may cause the system being unable to suspend * in case the PHY generates an interrupt when finishing the autonegotiation. * This interrupt may wake up the system immediately after suspend. * Therefore use sync = false only if you're sure it's safe with the respective * network chip. */ int phy_speed_down(struct phy_device *phydev, bool sync) { … } EXPORT_SYMBOL_GPL(…); /** * phy_speed_up - (re)set advertised speeds to all supported speeds * @phydev: the phy_device struct * * Description: Used to revert the effect of phy_speed_down */ int phy_speed_up(struct phy_device *phydev) { … } EXPORT_SYMBOL_GPL(…); /** * phy_start_machine - start PHY state machine tracking * @phydev: the phy_device struct * * Description: The PHY infrastructure can run a state machine * which tracks whether the PHY is starting up, negotiating, * etc. This function starts the delayed workqueue which tracks * the state of the PHY. If you want to maintain your own state machine, * do not call this function. */ void phy_start_machine(struct phy_device *phydev) { … } EXPORT_SYMBOL_GPL(…); /** * phy_stop_machine - stop the PHY state machine tracking * @phydev: target phy_device struct * * Description: Stops the state machine delayed workqueue, sets the * state to UP (unless it wasn't up yet). This function must be * called BEFORE phy_detach. */ void phy_stop_machine(struct phy_device *phydev) { … } static void phy_process_error(struct phy_device *phydev) { … } static void phy_error_precise(struct phy_device *phydev, const void *func, int err) { … } /** * phy_error - enter ERROR state for this PHY device * @phydev: target phy_device struct * * Moves the PHY to the ERROR state in response to a read * or write error, and tells the controller the link is down. * Must be called with phydev->lock held. */ void phy_error(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_disable_interrupts - Disable the PHY interrupts from the PHY side * @phydev: target phy_device struct */ int phy_disable_interrupts(struct phy_device *phydev) { … } /** * phy_interrupt - PHY interrupt handler * @irq: interrupt line * @phy_dat: phy_device pointer * * Description: Handle PHY interrupt */ static irqreturn_t phy_interrupt(int irq, void *phy_dat) { … } /** * phy_enable_interrupts - Enable the interrupts from the PHY side * @phydev: target phy_device struct */ static int phy_enable_interrupts(struct phy_device *phydev) { … } /** * phy_request_interrupt - request and enable interrupt for a PHY device * @phydev: target phy_device struct * * Description: Request and enable the interrupt for the given PHY. * If this fails, then we set irq to PHY_POLL. * This should only be called with a valid IRQ number. */ void phy_request_interrupt(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_free_interrupt - disable and free interrupt for a PHY device * @phydev: target phy_device struct * * Description: Disable and free the interrupt for the given PHY. * This should only be called with a valid IRQ number. */ void phy_free_interrupt(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); enum phy_state_work { … }; static enum phy_state_work _phy_state_machine(struct phy_device *phydev) { … } /* unlocked part of the PHY state machine */ static void _phy_state_machine_post_work(struct phy_device *phydev, enum phy_state_work state_work) { … } /** * phy_state_machine - Handle the state machine * @work: work_struct that describes the work to be done */ void phy_state_machine(struct work_struct *work) { … } /** * phy_stop - Bring down the PHY link, and stop checking the status * @phydev: target phy_device struct */ void phy_stop(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_start - start or restart a PHY device * @phydev: target phy_device struct * * Description: Indicates the attached device's readiness to * handle PHY-related work. Used during startup to start the * PHY, and after a call to phy_stop() to resume operation. * Also used to indicate the MDIO bus has cleared an error * condition. */ void phy_start(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_mac_interrupt - MAC says the link has changed * @phydev: phy_device struct with changed link * * The MAC layer is able to indicate there has been a change in the PHY link * status. Trigger the state machine and work a work queue. */ void phy_mac_interrupt(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_init_eee - init and check the EEE feature * @phydev: target phy_device struct * @clk_stop_enable: PHY may stop the clock during LPI * * Description: it checks if the Energy-Efficient Ethernet (EEE) * is supported by looking at the MMD registers 3.20 and 7.60/61 * and it programs the MMD register 3.0 setting the "Clock stop enable" * bit if required. */ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) { … } EXPORT_SYMBOL(…); /** * phy_get_eee_err - report the EEE wake error count * @phydev: target phy_device struct * * Description: it is to report the number of time where the PHY * failed to complete its normal wake sequence. */ int phy_get_eee_err(struct phy_device *phydev) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_get_eee - get EEE supported and status * @phydev: target phy_device struct * @data: ethtool_keee data * * Description: reports the Supported/Advertisement/LP Advertisement * capabilities, etc. */ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_keee *data) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_set_eee_noneg - Adjusts MAC LPI configuration without PHY * renegotiation * @phydev: pointer to the target PHY device structure * @data: pointer to the ethtool_keee structure containing the new EEE settings * * This function updates the Energy Efficient Ethernet (EEE) configuration * for cases where only the MAC's Low Power Idle (LPI) configuration changes, * without triggering PHY renegotiation. It ensures that the MAC is properly * informed of the new LPI settings by cycling the link down and up, which * is necessary for the MAC to adopt the new configuration. This adjustment * is done only if there is a change in the tx_lpi_enabled or tx_lpi_timer * configuration. */ static void phy_ethtool_set_eee_noneg(struct phy_device *phydev, struct ethtool_keee *data) { … } /** * phy_ethtool_set_eee - set EEE supported and status * @phydev: target phy_device struct * @data: ethtool_keee data * * Description: it is to program the Advertisement EEE register. */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_keee *data) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_set_wol - Configure Wake On LAN * * @phydev: target phy_device struct * @wol: Configuration requested */ int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_get_wol - Get the current Wake On LAN configuration * * @phydev: target phy_device struct * @wol: Store the current configuration here */ void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { … } EXPORT_SYMBOL(…); int phy_ethtool_get_link_ksettings(struct net_device *ndev, struct ethtool_link_ksettings *cmd) { … } EXPORT_SYMBOL(…); int phy_ethtool_set_link_ksettings(struct net_device *ndev, const struct ethtool_link_ksettings *cmd) { … } EXPORT_SYMBOL(…); /** * phy_ethtool_nway_reset - Restart auto negotiation * @ndev: Network device to restart autoneg for */ int phy_ethtool_nway_reset(struct net_device *ndev) { … } EXPORT_SYMBOL(…);