162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Framework for configuring and reading PHY devices 362306a36Sopenharmony_ci * Based on code in sungem_phy.c and gianfar_phy.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Andy Fleming 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2004 Freescale Semiconductor, Inc. 862306a36Sopenharmony_ci * Copyright (c) 2006, 2007 Maciej W. Rozycki 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/unistd.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/netdevice.h> 1862306a36Sopenharmony_ci#include <linux/netlink.h> 1962306a36Sopenharmony_ci#include <linux/etherdevice.h> 2062306a36Sopenharmony_ci#include <linux/skbuff.h> 2162306a36Sopenharmony_ci#include <linux/mm.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/mii.h> 2462306a36Sopenharmony_ci#include <linux/ethtool.h> 2562306a36Sopenharmony_ci#include <linux/ethtool_netlink.h> 2662306a36Sopenharmony_ci#include <linux/phy.h> 2762306a36Sopenharmony_ci#include <linux/phy_led_triggers.h> 2862306a36Sopenharmony_ci#include <linux/sfp.h> 2962306a36Sopenharmony_ci#include <linux/workqueue.h> 3062306a36Sopenharmony_ci#include <linux/mdio.h> 3162306a36Sopenharmony_ci#include <linux/io.h> 3262306a36Sopenharmony_ci#include <linux/uaccess.h> 3362306a36Sopenharmony_ci#include <linux/atomic.h> 3462306a36Sopenharmony_ci#include <linux/suspend.h> 3562306a36Sopenharmony_ci#include <net/netlink.h> 3662306a36Sopenharmony_ci#include <net/genetlink.h> 3762306a36Sopenharmony_ci#include <net/sock.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define PHY_STATE_TIME HZ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define PHY_STATE_STR(_state) \ 4262306a36Sopenharmony_ci case PHY_##_state: \ 4362306a36Sopenharmony_ci return __stringify(_state); \ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const char *phy_state_to_str(enum phy_state st) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci switch (st) { 4862306a36Sopenharmony_ci PHY_STATE_STR(DOWN) 4962306a36Sopenharmony_ci PHY_STATE_STR(READY) 5062306a36Sopenharmony_ci PHY_STATE_STR(UP) 5162306a36Sopenharmony_ci PHY_STATE_STR(RUNNING) 5262306a36Sopenharmony_ci PHY_STATE_STR(NOLINK) 5362306a36Sopenharmony_ci PHY_STATE_STR(CABLETEST) 5462306a36Sopenharmony_ci PHY_STATE_STR(HALTED) 5562306a36Sopenharmony_ci PHY_STATE_STR(ERROR) 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return NULL; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void phy_process_state_change(struct phy_device *phydev, 6262306a36Sopenharmony_ci enum phy_state old_state) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci if (old_state != phydev->state) { 6562306a36Sopenharmony_ci phydev_dbg(phydev, "PHY state change %s -> %s\n", 6662306a36Sopenharmony_ci phy_state_to_str(old_state), 6762306a36Sopenharmony_ci phy_state_to_str(phydev->state)); 6862306a36Sopenharmony_ci if (phydev->drv && phydev->drv->link_change_notify) 6962306a36Sopenharmony_ci phydev->drv->link_change_notify(phydev); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void phy_link_up(struct phy_device *phydev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci phydev->phy_link_change(phydev, true); 7662306a36Sopenharmony_ci phy_led_trigger_change_speed(phydev); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void phy_link_down(struct phy_device *phydev) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci phydev->phy_link_change(phydev, false); 8262306a36Sopenharmony_ci phy_led_trigger_change_speed(phydev); 8362306a36Sopenharmony_ci WRITE_ONCE(phydev->link_down_events, phydev->link_down_events + 1); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const char *phy_pause_str(struct phy_device *phydev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci bool local_pause, local_asym_pause; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (phydev->autoneg == AUTONEG_DISABLE) 9162306a36Sopenharmony_ci goto no_pause; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci local_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, 9462306a36Sopenharmony_ci phydev->advertising); 9562306a36Sopenharmony_ci local_asym_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 9662306a36Sopenharmony_ci phydev->advertising); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (local_pause && phydev->pause) 9962306a36Sopenharmony_ci return "rx/tx"; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (local_asym_pause && phydev->asym_pause) { 10262306a36Sopenharmony_ci if (local_pause) 10362306a36Sopenharmony_ci return "rx"; 10462306a36Sopenharmony_ci if (phydev->pause) 10562306a36Sopenharmony_ci return "tx"; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cino_pause: 10962306a36Sopenharmony_ci return "off"; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * phy_print_status - Convenience function to print out the current phy status 11462306a36Sopenharmony_ci * @phydev: the phy_device struct 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_civoid phy_print_status(struct phy_device *phydev) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci if (phydev->link) { 11962306a36Sopenharmony_ci netdev_info(phydev->attached_dev, 12062306a36Sopenharmony_ci "Link is Up - %s/%s %s- flow control %s\n", 12162306a36Sopenharmony_ci phy_speed_to_str(phydev->speed), 12262306a36Sopenharmony_ci phy_duplex_to_str(phydev->duplex), 12362306a36Sopenharmony_ci phydev->downshifted_rate ? "(downshifted) " : "", 12462306a36Sopenharmony_ci phy_pause_str(phydev)); 12562306a36Sopenharmony_ci } else { 12662306a36Sopenharmony_ci netdev_info(phydev->attached_dev, "Link is Down\n"); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ciEXPORT_SYMBOL(phy_print_status); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/** 13262306a36Sopenharmony_ci * phy_get_rate_matching - determine if rate matching is supported 13362306a36Sopenharmony_ci * @phydev: The phy device to return rate matching for 13462306a36Sopenharmony_ci * @iface: The interface mode to use 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * This determines the type of rate matching (if any) that @phy supports 13762306a36Sopenharmony_ci * using @iface. @iface may be %PHY_INTERFACE_MODE_NA to determine if any 13862306a36Sopenharmony_ci * interface supports rate matching. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Return: The type of rate matching @phy supports for @iface, or 14162306a36Sopenharmony_ci * %RATE_MATCH_NONE. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ciint phy_get_rate_matching(struct phy_device *phydev, 14462306a36Sopenharmony_ci phy_interface_t iface) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int ret = RATE_MATCH_NONE; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (phydev->drv->get_rate_matching) { 14962306a36Sopenharmony_ci mutex_lock(&phydev->lock); 15062306a36Sopenharmony_ci ret = phydev->drv->get_rate_matching(phydev, iface); 15162306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return ret; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_get_rate_matching); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/** 15962306a36Sopenharmony_ci * phy_config_interrupt - configure the PHY device for the requested interrupts 16062306a36Sopenharmony_ci * @phydev: the phy_device struct 16162306a36Sopenharmony_ci * @interrupts: interrupt flags to configure for this @phydev 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * Returns 0 on success or < 0 on error. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_cistatic int phy_config_interrupt(struct phy_device *phydev, bool interrupts) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci phydev->interrupts = interrupts ? 1 : 0; 16862306a36Sopenharmony_ci if (phydev->drv->config_intr) 16962306a36Sopenharmony_ci return phydev->drv->config_intr(phydev); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * phy_restart_aneg - restart auto-negotiation 17662306a36Sopenharmony_ci * @phydev: target phy_device struct 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Restart the autonegotiation on @phydev. Returns >= 0 on success or 17962306a36Sopenharmony_ci * negative errno on error. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ciint phy_restart_aneg(struct phy_device *phydev) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) 18662306a36Sopenharmony_ci ret = genphy_c45_restart_aneg(phydev); 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci ret = genphy_restart_aneg(phydev); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_restart_aneg); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/** 19562306a36Sopenharmony_ci * phy_aneg_done - return auto-negotiation status 19662306a36Sopenharmony_ci * @phydev: target phy_device struct 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * Description: Return the auto-negotiation status from this @phydev 19962306a36Sopenharmony_ci * Returns > 0 on success or < 0 on error. 0 means that auto-negotiation 20062306a36Sopenharmony_ci * is still pending. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ciint phy_aneg_done(struct phy_device *phydev) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci if (phydev->drv && phydev->drv->aneg_done) 20562306a36Sopenharmony_ci return phydev->drv->aneg_done(phydev); 20662306a36Sopenharmony_ci else if (phydev->is_c45) 20762306a36Sopenharmony_ci return genphy_c45_aneg_done(phydev); 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci return genphy_aneg_done(phydev); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciEXPORT_SYMBOL(phy_aneg_done); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * phy_find_valid - find a PHY setting that matches the requested parameters 21562306a36Sopenharmony_ci * @speed: desired speed 21662306a36Sopenharmony_ci * @duplex: desired duplex 21762306a36Sopenharmony_ci * @supported: mask of supported link modes 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * Locate a supported phy setting that is, in priority order: 22062306a36Sopenharmony_ci * - an exact match for the specified speed and duplex mode 22162306a36Sopenharmony_ci * - a match for the specified speed, or slower speed 22262306a36Sopenharmony_ci * - the slowest supported speed 22362306a36Sopenharmony_ci * Returns the matched phy_setting entry, or %NULL if no supported phy 22462306a36Sopenharmony_ci * settings were found. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_cistatic const struct phy_setting * 22762306a36Sopenharmony_ciphy_find_valid(int speed, int duplex, unsigned long *supported) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci return phy_lookup_setting(speed, duplex, supported, false); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/** 23362306a36Sopenharmony_ci * phy_supported_speeds - return all speeds currently supported by a phy device 23462306a36Sopenharmony_ci * @phy: The phy device to return supported speeds of. 23562306a36Sopenharmony_ci * @speeds: buffer to store supported speeds in. 23662306a36Sopenharmony_ci * @size: size of speeds buffer. 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * Description: Returns the number of supported speeds, and fills the speeds 23962306a36Sopenharmony_ci * buffer with the supported speeds. If speeds buffer is too small to contain 24062306a36Sopenharmony_ci * all currently supported speeds, will return as many speeds as can fit. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ciunsigned int phy_supported_speeds(struct phy_device *phy, 24362306a36Sopenharmony_ci unsigned int *speeds, 24462306a36Sopenharmony_ci unsigned int size) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return phy_speeds(speeds, size, phy->supported); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/** 25062306a36Sopenharmony_ci * phy_check_valid - check if there is a valid PHY setting which matches 25162306a36Sopenharmony_ci * speed, duplex, and feature mask 25262306a36Sopenharmony_ci * @speed: speed to match 25362306a36Sopenharmony_ci * @duplex: duplex to match 25462306a36Sopenharmony_ci * @features: A mask of the valid settings 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * Description: Returns true if there is a valid setting, false otherwise. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cibool phy_check_valid(int speed, int duplex, unsigned long *features) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci return !!phy_lookup_setting(speed, duplex, features, true); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ciEXPORT_SYMBOL(phy_check_valid); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/** 26562306a36Sopenharmony_ci * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex 26662306a36Sopenharmony_ci * @phydev: the target phy_device struct 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * Description: Make sure the PHY is set to supported speeds and 26962306a36Sopenharmony_ci * duplexes. Drop down by one in this order: 1000/FULL, 27062306a36Sopenharmony_ci * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistatic void phy_sanitize_settings(struct phy_device *phydev) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci const struct phy_setting *setting; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci setting = phy_find_valid(phydev->speed, phydev->duplex, 27762306a36Sopenharmony_ci phydev->supported); 27862306a36Sopenharmony_ci if (setting) { 27962306a36Sopenharmony_ci phydev->speed = setting->speed; 28062306a36Sopenharmony_ci phydev->duplex = setting->duplex; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci /* We failed to find anything (no supported speeds?) */ 28362306a36Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 28462306a36Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_civoid phy_ethtool_ksettings_get(struct phy_device *phydev, 28962306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci mutex_lock(&phydev->lock); 29262306a36Sopenharmony_ci linkmode_copy(cmd->link_modes.supported, phydev->supported); 29362306a36Sopenharmony_ci linkmode_copy(cmd->link_modes.advertising, phydev->advertising); 29462306a36Sopenharmony_ci linkmode_copy(cmd->link_modes.lp_advertising, phydev->lp_advertising); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci cmd->base.speed = phydev->speed; 29762306a36Sopenharmony_ci cmd->base.duplex = phydev->duplex; 29862306a36Sopenharmony_ci cmd->base.master_slave_cfg = phydev->master_slave_get; 29962306a36Sopenharmony_ci cmd->base.master_slave_state = phydev->master_slave_state; 30062306a36Sopenharmony_ci cmd->base.rate_matching = phydev->rate_matching; 30162306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_MOCA) 30262306a36Sopenharmony_ci cmd->base.port = PORT_BNC; 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci cmd->base.port = phydev->port; 30562306a36Sopenharmony_ci cmd->base.transceiver = phy_is_internal(phydev) ? 30662306a36Sopenharmony_ci XCVR_INTERNAL : XCVR_EXTERNAL; 30762306a36Sopenharmony_ci cmd->base.phy_address = phydev->mdio.addr; 30862306a36Sopenharmony_ci cmd->base.autoneg = phydev->autoneg; 30962306a36Sopenharmony_ci cmd->base.eth_tp_mdix_ctrl = phydev->mdix_ctrl; 31062306a36Sopenharmony_ci cmd->base.eth_tp_mdix = phydev->mdix; 31162306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_ksettings_get); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/** 31662306a36Sopenharmony_ci * phy_mii_ioctl - generic PHY MII ioctl interface 31762306a36Sopenharmony_ci * @phydev: the phy_device struct 31862306a36Sopenharmony_ci * @ifr: &struct ifreq for socket ioctl's 31962306a36Sopenharmony_ci * @cmd: ioctl cmd to execute 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * Note that this function is currently incompatible with the 32262306a36Sopenharmony_ci * PHYCONTROL layer. It changes registers without regard to 32362306a36Sopenharmony_ci * current state. Use at own risk. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ciint phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct mii_ioctl_data *mii_data = if_mii(ifr); 32862306a36Sopenharmony_ci u16 val = mii_data->val_in; 32962306a36Sopenharmony_ci bool change_autoneg = false; 33062306a36Sopenharmony_ci int prtad, devad; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci switch (cmd) { 33362306a36Sopenharmony_ci case SIOCGMIIPHY: 33462306a36Sopenharmony_ci mii_data->phy_id = phydev->mdio.addr; 33562306a36Sopenharmony_ci fallthrough; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci case SIOCGMIIREG: 33862306a36Sopenharmony_ci if (mdio_phy_id_is_c45(mii_data->phy_id)) { 33962306a36Sopenharmony_ci prtad = mdio_phy_id_prtad(mii_data->phy_id); 34062306a36Sopenharmony_ci devad = mdio_phy_id_devad(mii_data->phy_id); 34162306a36Sopenharmony_ci mii_data->val_out = mdiobus_c45_read( 34262306a36Sopenharmony_ci phydev->mdio.bus, prtad, devad, 34362306a36Sopenharmony_ci mii_data->reg_num); 34462306a36Sopenharmony_ci } else { 34562306a36Sopenharmony_ci mii_data->val_out = mdiobus_read( 34662306a36Sopenharmony_ci phydev->mdio.bus, mii_data->phy_id, 34762306a36Sopenharmony_ci mii_data->reg_num); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci case SIOCSMIIREG: 35262306a36Sopenharmony_ci if (mdio_phy_id_is_c45(mii_data->phy_id)) { 35362306a36Sopenharmony_ci prtad = mdio_phy_id_prtad(mii_data->phy_id); 35462306a36Sopenharmony_ci devad = mdio_phy_id_devad(mii_data->phy_id); 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci prtad = mii_data->phy_id; 35762306a36Sopenharmony_ci devad = mii_data->reg_num; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci if (prtad == phydev->mdio.addr) { 36062306a36Sopenharmony_ci switch (devad) { 36162306a36Sopenharmony_ci case MII_BMCR: 36262306a36Sopenharmony_ci if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) { 36362306a36Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE) 36462306a36Sopenharmony_ci change_autoneg = true; 36562306a36Sopenharmony_ci phydev->autoneg = AUTONEG_DISABLE; 36662306a36Sopenharmony_ci if (val & BMCR_FULLDPLX) 36762306a36Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci phydev->duplex = DUPLEX_HALF; 37062306a36Sopenharmony_ci if (val & BMCR_SPEED1000) 37162306a36Sopenharmony_ci phydev->speed = SPEED_1000; 37262306a36Sopenharmony_ci else if (val & BMCR_SPEED100) 37362306a36Sopenharmony_ci phydev->speed = SPEED_100; 37462306a36Sopenharmony_ci else phydev->speed = SPEED_10; 37562306a36Sopenharmony_ci } else { 37662306a36Sopenharmony_ci if (phydev->autoneg == AUTONEG_DISABLE) 37762306a36Sopenharmony_ci change_autoneg = true; 37862306a36Sopenharmony_ci phydev->autoneg = AUTONEG_ENABLE; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci case MII_ADVERTISE: 38262306a36Sopenharmony_ci mii_adv_mod_linkmode_adv_t(phydev->advertising, 38362306a36Sopenharmony_ci val); 38462306a36Sopenharmony_ci change_autoneg = true; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case MII_CTRL1000: 38762306a36Sopenharmony_ci mii_ctrl1000_mod_linkmode_adv_t(phydev->advertising, 38862306a36Sopenharmony_ci val); 38962306a36Sopenharmony_ci change_autoneg = true; 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci /* do nothing */ 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (mdio_phy_id_is_c45(mii_data->phy_id)) 39862306a36Sopenharmony_ci mdiobus_c45_write(phydev->mdio.bus, prtad, devad, 39962306a36Sopenharmony_ci mii_data->reg_num, val); 40062306a36Sopenharmony_ci else 40162306a36Sopenharmony_ci mdiobus_write(phydev->mdio.bus, prtad, devad, val); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (prtad == phydev->mdio.addr && 40462306a36Sopenharmony_ci devad == MII_BMCR && 40562306a36Sopenharmony_ci val & BMCR_RESET) 40662306a36Sopenharmony_ci return phy_init_hw(phydev); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (change_autoneg) 40962306a36Sopenharmony_ci return phy_start_aneg(phydev); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci case SIOCSHWTSTAMP: 41462306a36Sopenharmony_ci if (phydev->mii_ts && phydev->mii_ts->hwtstamp) 41562306a36Sopenharmony_ci return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr); 41662306a36Sopenharmony_ci fallthrough; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci default: 41962306a36Sopenharmony_ci return -EOPNOTSUPP; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ciEXPORT_SYMBOL(phy_mii_ioctl); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/** 42562306a36Sopenharmony_ci * phy_do_ioctl - generic ndo_eth_ioctl implementation 42662306a36Sopenharmony_ci * @dev: the net_device struct 42762306a36Sopenharmony_ci * @ifr: &struct ifreq for socket ioctl's 42862306a36Sopenharmony_ci * @cmd: ioctl cmd to execute 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ciint phy_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci if (!dev->phydev) 43362306a36Sopenharmony_ci return -ENODEV; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return phy_mii_ioctl(dev->phydev, ifr, cmd); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ciEXPORT_SYMBOL(phy_do_ioctl); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/** 44062306a36Sopenharmony_ci * phy_do_ioctl_running - generic ndo_eth_ioctl implementation but test first 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * @dev: the net_device struct 44362306a36Sopenharmony_ci * @ifr: &struct ifreq for socket ioctl's 44462306a36Sopenharmony_ci * @cmd: ioctl cmd to execute 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * Same as phy_do_ioctl, but ensures that net_device is running before 44762306a36Sopenharmony_ci * handling the ioctl. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ciint phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci if (!netif_running(dev)) 45262306a36Sopenharmony_ci return -ENODEV; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return phy_do_ioctl(dev, ifr, cmd); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ciEXPORT_SYMBOL(phy_do_ioctl_running); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci/** 45962306a36Sopenharmony_ci * __phy_hwtstamp_get - Get hardware timestamping configuration from PHY 46062306a36Sopenharmony_ci * 46162306a36Sopenharmony_ci * @phydev: the PHY device structure 46262306a36Sopenharmony_ci * @config: structure holding the timestamping configuration 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * Query the PHY device for its current hardware timestamping configuration. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ciint __phy_hwtstamp_get(struct phy_device *phydev, 46762306a36Sopenharmony_ci struct kernel_hwtstamp_config *config) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci if (!phydev) 47062306a36Sopenharmony_ci return -ENODEV; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return phy_mii_ioctl(phydev, config->ifr, SIOCGHWTSTAMP); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/** 47662306a36Sopenharmony_ci * __phy_hwtstamp_set - Modify PHY hardware timestamping configuration 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * @phydev: the PHY device structure 47962306a36Sopenharmony_ci * @config: structure holding the timestamping configuration 48062306a36Sopenharmony_ci * @extack: netlink extended ack structure, for error reporting 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ciint __phy_hwtstamp_set(struct phy_device *phydev, 48362306a36Sopenharmony_ci struct kernel_hwtstamp_config *config, 48462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci if (!phydev) 48762306a36Sopenharmony_ci return -ENODEV; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return phy_mii_ioctl(phydev, config->ifr, SIOCSHWTSTAMP); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/** 49362306a36Sopenharmony_ci * phy_queue_state_machine - Trigger the state machine to run soon 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * @phydev: the phy_device struct 49662306a36Sopenharmony_ci * @jiffies: Run the state machine after these jiffies 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_civoid phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci mod_delayed_work(system_power_efficient_wq, &phydev->state_queue, 50162306a36Sopenharmony_ci jiffies); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ciEXPORT_SYMBOL(phy_queue_state_machine); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/** 50662306a36Sopenharmony_ci * phy_trigger_machine - Trigger the state machine to run now 50762306a36Sopenharmony_ci * 50862306a36Sopenharmony_ci * @phydev: the phy_device struct 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_civoid phy_trigger_machine(struct phy_device *phydev) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci phy_queue_state_machine(phydev, 0); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ciEXPORT_SYMBOL(phy_trigger_machine); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void phy_abort_cable_test(struct phy_device *phydev) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int err; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ethnl_cable_test_finished(phydev); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci err = phy_init_hw(phydev); 52362306a36Sopenharmony_ci if (err) 52462306a36Sopenharmony_ci phydev_err(phydev, "Error while aborting cable test"); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * phy_ethtool_get_strings - Get the statistic counter names 52962306a36Sopenharmony_ci * 53062306a36Sopenharmony_ci * @phydev: the phy_device struct 53162306a36Sopenharmony_ci * @data: Where to put the strings 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ciint phy_ethtool_get_strings(struct phy_device *phydev, u8 *data) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci if (!phydev->drv) 53662306a36Sopenharmony_ci return -EIO; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci mutex_lock(&phydev->lock); 53962306a36Sopenharmony_ci phydev->drv->get_strings(phydev, data); 54062306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_strings); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/** 54762306a36Sopenharmony_ci * phy_ethtool_get_sset_count - Get the number of statistic counters 54862306a36Sopenharmony_ci * 54962306a36Sopenharmony_ci * @phydev: the phy_device struct 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ciint phy_ethtool_get_sset_count(struct phy_device *phydev) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci int ret; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (!phydev->drv) 55662306a36Sopenharmony_ci return -EIO; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (phydev->drv->get_sset_count && 55962306a36Sopenharmony_ci phydev->drv->get_strings && 56062306a36Sopenharmony_ci phydev->drv->get_stats) { 56162306a36Sopenharmony_ci mutex_lock(&phydev->lock); 56262306a36Sopenharmony_ci ret = phydev->drv->get_sset_count(phydev); 56362306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return -EOPNOTSUPP; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_sset_count); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/** 57362306a36Sopenharmony_ci * phy_ethtool_get_stats - Get the statistic counters 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci * @phydev: the phy_device struct 57662306a36Sopenharmony_ci * @stats: What counters to get 57762306a36Sopenharmony_ci * @data: Where to store the counters 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ciint phy_ethtool_get_stats(struct phy_device *phydev, 58062306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci if (!phydev->drv) 58362306a36Sopenharmony_ci return -EIO; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci mutex_lock(&phydev->lock); 58662306a36Sopenharmony_ci phydev->drv->get_stats(phydev, stats, data); 58762306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_stats); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/** 59462306a36Sopenharmony_ci * phy_ethtool_get_plca_cfg - Get PLCA RS configuration 59562306a36Sopenharmony_ci * @phydev: the phy_device struct 59662306a36Sopenharmony_ci * @plca_cfg: where to store the retrieved configuration 59762306a36Sopenharmony_ci * 59862306a36Sopenharmony_ci * Retrieve the PLCA configuration from the PHY. Return 0 on success or a 59962306a36Sopenharmony_ci * negative value if an error occurred. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ciint phy_ethtool_get_plca_cfg(struct phy_device *phydev, 60262306a36Sopenharmony_ci struct phy_plca_cfg *plca_cfg) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (!phydev->drv) { 60762306a36Sopenharmony_ci ret = -EIO; 60862306a36Sopenharmony_ci goto out; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (!phydev->drv->get_plca_cfg) { 61262306a36Sopenharmony_ci ret = -EOPNOTSUPP; 61362306a36Sopenharmony_ci goto out; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci mutex_lock(&phydev->lock); 61762306a36Sopenharmony_ci ret = phydev->drv->get_plca_cfg(phydev, plca_cfg); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 62062306a36Sopenharmony_ciout: 62162306a36Sopenharmony_ci return ret; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/** 62562306a36Sopenharmony_ci * plca_check_valid - Check PLCA configuration before enabling 62662306a36Sopenharmony_ci * @phydev: the phy_device struct 62762306a36Sopenharmony_ci * @plca_cfg: current PLCA configuration 62862306a36Sopenharmony_ci * @extack: extack for reporting useful error messages 62962306a36Sopenharmony_ci * 63062306a36Sopenharmony_ci * Checks whether the PLCA and PHY configuration are consistent and it is safe 63162306a36Sopenharmony_ci * to enable PLCA. Returns 0 on success or a negative value if the PLCA or PHY 63262306a36Sopenharmony_ci * configuration is not consistent. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_cistatic int plca_check_valid(struct phy_device *phydev, 63562306a36Sopenharmony_ci const struct phy_plca_cfg *plca_cfg, 63662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci int ret = 0; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, 64162306a36Sopenharmony_ci phydev->advertising)) { 64262306a36Sopenharmony_ci ret = -EOPNOTSUPP; 64362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 64462306a36Sopenharmony_ci "Point to Multi-Point mode is not enabled"); 64562306a36Sopenharmony_ci } else if (plca_cfg->node_id >= 255) { 64662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "PLCA node ID is not set"); 64762306a36Sopenharmony_ci ret = -EINVAL; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return ret; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/** 65462306a36Sopenharmony_ci * phy_ethtool_set_plca_cfg - Set PLCA RS configuration 65562306a36Sopenharmony_ci * @phydev: the phy_device struct 65662306a36Sopenharmony_ci * @plca_cfg: new PLCA configuration to apply 65762306a36Sopenharmony_ci * @extack: extack for reporting useful error messages 65862306a36Sopenharmony_ci * 65962306a36Sopenharmony_ci * Sets the PLCA configuration in the PHY. Return 0 on success or a 66062306a36Sopenharmony_ci * negative value if an error occurred. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ciint phy_ethtool_set_plca_cfg(struct phy_device *phydev, 66362306a36Sopenharmony_ci const struct phy_plca_cfg *plca_cfg, 66462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct phy_plca_cfg *curr_plca_cfg; 66762306a36Sopenharmony_ci int ret; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!phydev->drv) { 67062306a36Sopenharmony_ci ret = -EIO; 67162306a36Sopenharmony_ci goto out; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (!phydev->drv->set_plca_cfg || 67562306a36Sopenharmony_ci !phydev->drv->get_plca_cfg) { 67662306a36Sopenharmony_ci ret = -EOPNOTSUPP; 67762306a36Sopenharmony_ci goto out; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci curr_plca_cfg = kmalloc(sizeof(*curr_plca_cfg), GFP_KERNEL); 68162306a36Sopenharmony_ci if (!curr_plca_cfg) { 68262306a36Sopenharmony_ci ret = -ENOMEM; 68362306a36Sopenharmony_ci goto out; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci mutex_lock(&phydev->lock); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci ret = phydev->drv->get_plca_cfg(phydev, curr_plca_cfg); 68962306a36Sopenharmony_ci if (ret) 69062306a36Sopenharmony_ci goto out_drv; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (curr_plca_cfg->enabled < 0 && plca_cfg->enabled >= 0) { 69362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 69462306a36Sopenharmony_ci "PHY does not support changing the PLCA 'enable' attribute"); 69562306a36Sopenharmony_ci ret = -EINVAL; 69662306a36Sopenharmony_ci goto out_drv; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (curr_plca_cfg->node_id < 0 && plca_cfg->node_id >= 0) { 70062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 70162306a36Sopenharmony_ci "PHY does not support changing the PLCA 'local node ID' attribute"); 70262306a36Sopenharmony_ci ret = -EINVAL; 70362306a36Sopenharmony_ci goto out_drv; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (curr_plca_cfg->node_cnt < 0 && plca_cfg->node_cnt >= 0) { 70762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 70862306a36Sopenharmony_ci "PHY does not support changing the PLCA 'node count' attribute"); 70962306a36Sopenharmony_ci ret = -EINVAL; 71062306a36Sopenharmony_ci goto out_drv; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (curr_plca_cfg->to_tmr < 0 && plca_cfg->to_tmr >= 0) { 71462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 71562306a36Sopenharmony_ci "PHY does not support changing the PLCA 'TO timer' attribute"); 71662306a36Sopenharmony_ci ret = -EINVAL; 71762306a36Sopenharmony_ci goto out_drv; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (curr_plca_cfg->burst_cnt < 0 && plca_cfg->burst_cnt >= 0) { 72162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 72262306a36Sopenharmony_ci "PHY does not support changing the PLCA 'burst count' attribute"); 72362306a36Sopenharmony_ci ret = -EINVAL; 72462306a36Sopenharmony_ci goto out_drv; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (curr_plca_cfg->burst_tmr < 0 && plca_cfg->burst_tmr >= 0) { 72862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 72962306a36Sopenharmony_ci "PHY does not support changing the PLCA 'burst timer' attribute"); 73062306a36Sopenharmony_ci ret = -EINVAL; 73162306a36Sopenharmony_ci goto out_drv; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci // if enabling PLCA, perform a few sanity checks 73562306a36Sopenharmony_ci if (plca_cfg->enabled > 0) { 73662306a36Sopenharmony_ci // allow setting node_id concurrently with enabled 73762306a36Sopenharmony_ci if (plca_cfg->node_id >= 0) 73862306a36Sopenharmony_ci curr_plca_cfg->node_id = plca_cfg->node_id; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci ret = plca_check_valid(phydev, curr_plca_cfg, extack); 74162306a36Sopenharmony_ci if (ret) 74262306a36Sopenharmony_ci goto out_drv; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ret = phydev->drv->set_plca_cfg(phydev, plca_cfg); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ciout_drv: 74862306a36Sopenharmony_ci kfree(curr_plca_cfg); 74962306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 75062306a36Sopenharmony_ciout: 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci/** 75562306a36Sopenharmony_ci * phy_ethtool_get_plca_status - Get PLCA RS status information 75662306a36Sopenharmony_ci * @phydev: the phy_device struct 75762306a36Sopenharmony_ci * @plca_st: where to store the retrieved status information 75862306a36Sopenharmony_ci * 75962306a36Sopenharmony_ci * Retrieve the PLCA status information from the PHY. Return 0 on success or a 76062306a36Sopenharmony_ci * negative value if an error occurred. 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ciint phy_ethtool_get_plca_status(struct phy_device *phydev, 76362306a36Sopenharmony_ci struct phy_plca_status *plca_st) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci int ret; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!phydev->drv) { 76862306a36Sopenharmony_ci ret = -EIO; 76962306a36Sopenharmony_ci goto out; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (!phydev->drv->get_plca_status) { 77362306a36Sopenharmony_ci ret = -EOPNOTSUPP; 77462306a36Sopenharmony_ci goto out; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mutex_lock(&phydev->lock); 77862306a36Sopenharmony_ci ret = phydev->drv->get_plca_status(phydev, plca_st); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 78162306a36Sopenharmony_ciout: 78262306a36Sopenharmony_ci return ret; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/** 78662306a36Sopenharmony_ci * phy_start_cable_test - Start a cable test 78762306a36Sopenharmony_ci * 78862306a36Sopenharmony_ci * @phydev: the phy_device struct 78962306a36Sopenharmony_ci * @extack: extack for reporting useful error messages 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ciint phy_start_cable_test(struct phy_device *phydev, 79262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 79562306a36Sopenharmony_ci int err = -ENOMEM; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (!(phydev->drv && 79862306a36Sopenharmony_ci phydev->drv->cable_test_start && 79962306a36Sopenharmony_ci phydev->drv->cable_test_get_status)) { 80062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 80162306a36Sopenharmony_ci "PHY driver does not support cable testing"); 80262306a36Sopenharmony_ci return -EOPNOTSUPP; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci mutex_lock(&phydev->lock); 80662306a36Sopenharmony_ci if (phydev->state == PHY_CABLETEST) { 80762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 80862306a36Sopenharmony_ci "PHY already performing a test"); 80962306a36Sopenharmony_ci err = -EBUSY; 81062306a36Sopenharmony_ci goto out; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (phydev->state < PHY_UP || 81462306a36Sopenharmony_ci phydev->state > PHY_CABLETEST) { 81562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 81662306a36Sopenharmony_ci "PHY not configured. Try setting interface up"); 81762306a36Sopenharmony_ci err = -EBUSY; 81862306a36Sopenharmony_ci goto out; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_NTF); 82262306a36Sopenharmony_ci if (err) 82362306a36Sopenharmony_ci goto out; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Mark the carrier down until the test is complete */ 82662306a36Sopenharmony_ci phy_link_down(phydev); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci netif_testing_on(dev); 82962306a36Sopenharmony_ci err = phydev->drv->cable_test_start(phydev); 83062306a36Sopenharmony_ci if (err) { 83162306a36Sopenharmony_ci netif_testing_off(dev); 83262306a36Sopenharmony_ci phy_link_up(phydev); 83362306a36Sopenharmony_ci goto out_free; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci phydev->state = PHY_CABLETEST; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (phy_polling_mode(phydev)) 83962306a36Sopenharmony_ci phy_trigger_machine(phydev); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ciout_free: 84662306a36Sopenharmony_ci ethnl_cable_test_free(phydev); 84762306a36Sopenharmony_ciout: 84862306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return err; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ciEXPORT_SYMBOL(phy_start_cable_test); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/** 85562306a36Sopenharmony_ci * phy_start_cable_test_tdr - Start a raw TDR cable test 85662306a36Sopenharmony_ci * 85762306a36Sopenharmony_ci * @phydev: the phy_device struct 85862306a36Sopenharmony_ci * @extack: extack for reporting useful error messages 85962306a36Sopenharmony_ci * @config: Configuration of the test to run 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_ciint phy_start_cable_test_tdr(struct phy_device *phydev, 86262306a36Sopenharmony_ci struct netlink_ext_ack *extack, 86362306a36Sopenharmony_ci const struct phy_tdr_config *config) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 86662306a36Sopenharmony_ci int err = -ENOMEM; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (!(phydev->drv && 86962306a36Sopenharmony_ci phydev->drv->cable_test_tdr_start && 87062306a36Sopenharmony_ci phydev->drv->cable_test_get_status)) { 87162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 87262306a36Sopenharmony_ci "PHY driver does not support cable test TDR"); 87362306a36Sopenharmony_ci return -EOPNOTSUPP; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci mutex_lock(&phydev->lock); 87762306a36Sopenharmony_ci if (phydev->state == PHY_CABLETEST) { 87862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 87962306a36Sopenharmony_ci "PHY already performing a test"); 88062306a36Sopenharmony_ci err = -EBUSY; 88162306a36Sopenharmony_ci goto out; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (phydev->state < PHY_UP || 88562306a36Sopenharmony_ci phydev->state > PHY_CABLETEST) { 88662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 88762306a36Sopenharmony_ci "PHY not configured. Try setting interface up"); 88862306a36Sopenharmony_ci err = -EBUSY; 88962306a36Sopenharmony_ci goto out; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 89362306a36Sopenharmony_ci if (err) 89462306a36Sopenharmony_ci goto out; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* Mark the carrier down until the test is complete */ 89762306a36Sopenharmony_ci phy_link_down(phydev); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci netif_testing_on(dev); 90062306a36Sopenharmony_ci err = phydev->drv->cable_test_tdr_start(phydev, config); 90162306a36Sopenharmony_ci if (err) { 90262306a36Sopenharmony_ci netif_testing_off(dev); 90362306a36Sopenharmony_ci phy_link_up(phydev); 90462306a36Sopenharmony_ci goto out_free; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci phydev->state = PHY_CABLETEST; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (phy_polling_mode(phydev)) 91062306a36Sopenharmony_ci phy_trigger_machine(phydev); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ciout_free: 91762306a36Sopenharmony_ci ethnl_cable_test_free(phydev); 91862306a36Sopenharmony_ciout: 91962306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return err; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ciEXPORT_SYMBOL(phy_start_cable_test_tdr); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ciint phy_config_aneg(struct phy_device *phydev) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci if (phydev->drv->config_aneg) 92862306a36Sopenharmony_ci return phydev->drv->config_aneg(phydev); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Clause 45 PHYs that don't implement Clause 22 registers are not 93162306a36Sopenharmony_ci * allowed to call genphy_config_aneg() 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_ci if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) 93462306a36Sopenharmony_ci return genphy_c45_config_aneg(phydev); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return genphy_config_aneg(phydev); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ciEXPORT_SYMBOL(phy_config_aneg); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci/** 94162306a36Sopenharmony_ci * phy_check_link_status - check link status and set state accordingly 94262306a36Sopenharmony_ci * @phydev: the phy_device struct 94362306a36Sopenharmony_ci * 94462306a36Sopenharmony_ci * Description: Check for link and whether autoneg was triggered / is running 94562306a36Sopenharmony_ci * and set state accordingly 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_cistatic int phy_check_link_status(struct phy_device *phydev) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci int err; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci lockdep_assert_held(&phydev->lock); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* Keep previous state if loopback is enabled because some PHYs 95462306a36Sopenharmony_ci * report that Link is Down when loopback is enabled. 95562306a36Sopenharmony_ci */ 95662306a36Sopenharmony_ci if (phydev->loopback_enabled) 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci err = phy_read_status(phydev); 96062306a36Sopenharmony_ci if (err) 96162306a36Sopenharmony_ci return err; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (phydev->link && phydev->state != PHY_RUNNING) { 96462306a36Sopenharmony_ci phy_check_downshift(phydev); 96562306a36Sopenharmony_ci phydev->state = PHY_RUNNING; 96662306a36Sopenharmony_ci phy_link_up(phydev); 96762306a36Sopenharmony_ci } else if (!phydev->link && phydev->state != PHY_NOLINK) { 96862306a36Sopenharmony_ci phydev->state = PHY_NOLINK; 96962306a36Sopenharmony_ci phy_link_down(phydev); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/** 97662306a36Sopenharmony_ci * _phy_start_aneg - start auto-negotiation for this PHY device 97762306a36Sopenharmony_ci * @phydev: the phy_device struct 97862306a36Sopenharmony_ci * 97962306a36Sopenharmony_ci * Description: Sanitizes the settings (if we're not autonegotiating 98062306a36Sopenharmony_ci * them), and then calls the driver's config_aneg function. 98162306a36Sopenharmony_ci * If the PHYCONTROL Layer is operating, we change the state to 98262306a36Sopenharmony_ci * reflect the beginning of Auto-negotiation or forcing. 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_cistatic int _phy_start_aneg(struct phy_device *phydev) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci int err; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci lockdep_assert_held(&phydev->lock); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (!phydev->drv) 99162306a36Sopenharmony_ci return -EIO; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (AUTONEG_DISABLE == phydev->autoneg) 99462306a36Sopenharmony_ci phy_sanitize_settings(phydev); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci err = phy_config_aneg(phydev); 99762306a36Sopenharmony_ci if (err < 0) 99862306a36Sopenharmony_ci return err; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (phy_is_started(phydev)) 100162306a36Sopenharmony_ci err = phy_check_link_status(phydev); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return err; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci/** 100762306a36Sopenharmony_ci * phy_start_aneg - start auto-negotiation for this PHY device 100862306a36Sopenharmony_ci * @phydev: the phy_device struct 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * Description: Sanitizes the settings (if we're not autonegotiating 101162306a36Sopenharmony_ci * them), and then calls the driver's config_aneg function. 101262306a36Sopenharmony_ci * If the PHYCONTROL Layer is operating, we change the state to 101362306a36Sopenharmony_ci * reflect the beginning of Auto-negotiation or forcing. 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ciint phy_start_aneg(struct phy_device *phydev) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci int err; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci mutex_lock(&phydev->lock); 102062306a36Sopenharmony_ci err = _phy_start_aneg(phydev); 102162306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return err; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ciEXPORT_SYMBOL(phy_start_aneg); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int phy_poll_aneg_done(struct phy_device *phydev) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci unsigned int retries = 100; 103062306a36Sopenharmony_ci int ret; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci do { 103362306a36Sopenharmony_ci msleep(100); 103462306a36Sopenharmony_ci ret = phy_aneg_done(phydev); 103562306a36Sopenharmony_ci } while (!ret && --retries); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (!ret) 103862306a36Sopenharmony_ci return -ETIMEDOUT; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci return ret < 0 ? ret : 0; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ciint phy_ethtool_ksettings_set(struct phy_device *phydev, 104462306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); 104762306a36Sopenharmony_ci u8 autoneg = cmd->base.autoneg; 104862306a36Sopenharmony_ci u8 duplex = cmd->base.duplex; 104962306a36Sopenharmony_ci u32 speed = cmd->base.speed; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (cmd->base.phy_address != phydev->mdio.addr) 105262306a36Sopenharmony_ci return -EINVAL; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci linkmode_copy(advertising, cmd->link_modes.advertising); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* We make sure that we don't pass unsupported values in to the PHY */ 105762306a36Sopenharmony_ci linkmode_and(advertising, advertising, phydev->supported); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* Verify the settings we care about. */ 106062306a36Sopenharmony_ci if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE) 106162306a36Sopenharmony_ci return -EINVAL; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (autoneg == AUTONEG_ENABLE && linkmode_empty(advertising)) 106462306a36Sopenharmony_ci return -EINVAL; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (autoneg == AUTONEG_DISABLE && 106762306a36Sopenharmony_ci ((speed != SPEED_1000 && 106862306a36Sopenharmony_ci speed != SPEED_100 && 106962306a36Sopenharmony_ci speed != SPEED_10) || 107062306a36Sopenharmony_ci (duplex != DUPLEX_HALF && 107162306a36Sopenharmony_ci duplex != DUPLEX_FULL))) 107262306a36Sopenharmony_ci return -EINVAL; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci mutex_lock(&phydev->lock); 107562306a36Sopenharmony_ci phydev->autoneg = autoneg; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (autoneg == AUTONEG_DISABLE) { 107862306a36Sopenharmony_ci phydev->speed = speed; 107962306a36Sopenharmony_ci phydev->duplex = duplex; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci linkmode_copy(phydev->advertising, advertising); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 108562306a36Sopenharmony_ci phydev->advertising, autoneg == AUTONEG_ENABLE); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci phydev->master_slave_set = cmd->base.master_slave_cfg; 108862306a36Sopenharmony_ci phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* Restart the PHY */ 109162306a36Sopenharmony_ci if (phy_is_started(phydev)) { 109262306a36Sopenharmony_ci phydev->state = PHY_UP; 109362306a36Sopenharmony_ci phy_trigger_machine(phydev); 109462306a36Sopenharmony_ci } else { 109562306a36Sopenharmony_ci _phy_start_aneg(phydev); 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 109962306a36Sopenharmony_ci return 0; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_ksettings_set); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci/** 110462306a36Sopenharmony_ci * phy_speed_down - set speed to lowest speed supported by both link partners 110562306a36Sopenharmony_ci * @phydev: the phy_device struct 110662306a36Sopenharmony_ci * @sync: perform action synchronously 110762306a36Sopenharmony_ci * 110862306a36Sopenharmony_ci * Description: Typically used to save energy when waiting for a WoL packet 110962306a36Sopenharmony_ci * 111062306a36Sopenharmony_ci * WARNING: Setting sync to false may cause the system being unable to suspend 111162306a36Sopenharmony_ci * in case the PHY generates an interrupt when finishing the autonegotiation. 111262306a36Sopenharmony_ci * This interrupt may wake up the system immediately after suspend. 111362306a36Sopenharmony_ci * Therefore use sync = false only if you're sure it's safe with the respective 111462306a36Sopenharmony_ci * network chip. 111562306a36Sopenharmony_ci */ 111662306a36Sopenharmony_ciint phy_speed_down(struct phy_device *phydev, bool sync) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_tmp); 111962306a36Sopenharmony_ci int ret = 0; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci mutex_lock(&phydev->lock); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE) 112462306a36Sopenharmony_ci goto out; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci linkmode_copy(adv_tmp, phydev->advertising); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ret = phy_speed_down_core(phydev); 112962306a36Sopenharmony_ci if (ret) 113062306a36Sopenharmony_ci goto out; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci linkmode_copy(phydev->adv_old, adv_tmp); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (linkmode_equal(phydev->advertising, adv_tmp)) { 113562306a36Sopenharmony_ci ret = 0; 113662306a36Sopenharmony_ci goto out; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci ret = phy_config_aneg(phydev); 114062306a36Sopenharmony_ci if (ret) 114162306a36Sopenharmony_ci goto out; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci ret = sync ? phy_poll_aneg_done(phydev) : 0; 114462306a36Sopenharmony_ciout: 114562306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci return ret; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_speed_down); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/** 115262306a36Sopenharmony_ci * phy_speed_up - (re)set advertised speeds to all supported speeds 115362306a36Sopenharmony_ci * @phydev: the phy_device struct 115462306a36Sopenharmony_ci * 115562306a36Sopenharmony_ci * Description: Used to revert the effect of phy_speed_down 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_ciint phy_speed_up(struct phy_device *phydev) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_tmp); 116062306a36Sopenharmony_ci int ret = 0; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci mutex_lock(&phydev->lock); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE) 116562306a36Sopenharmony_ci goto out; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (linkmode_empty(phydev->adv_old)) 116862306a36Sopenharmony_ci goto out; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci linkmode_copy(adv_tmp, phydev->advertising); 117162306a36Sopenharmony_ci linkmode_copy(phydev->advertising, phydev->adv_old); 117262306a36Sopenharmony_ci linkmode_zero(phydev->adv_old); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (linkmode_equal(phydev->advertising, adv_tmp)) 117562306a36Sopenharmony_ci goto out; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci ret = phy_config_aneg(phydev); 117862306a36Sopenharmony_ciout: 117962306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci return ret; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_speed_up); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/** 118662306a36Sopenharmony_ci * phy_start_machine - start PHY state machine tracking 118762306a36Sopenharmony_ci * @phydev: the phy_device struct 118862306a36Sopenharmony_ci * 118962306a36Sopenharmony_ci * Description: The PHY infrastructure can run a state machine 119062306a36Sopenharmony_ci * which tracks whether the PHY is starting up, negotiating, 119162306a36Sopenharmony_ci * etc. This function starts the delayed workqueue which tracks 119262306a36Sopenharmony_ci * the state of the PHY. If you want to maintain your own state machine, 119362306a36Sopenharmony_ci * do not call this function. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_civoid phy_start_machine(struct phy_device *phydev) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci phy_trigger_machine(phydev); 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_start_machine); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci/** 120262306a36Sopenharmony_ci * phy_stop_machine - stop the PHY state machine tracking 120362306a36Sopenharmony_ci * @phydev: target phy_device struct 120462306a36Sopenharmony_ci * 120562306a36Sopenharmony_ci * Description: Stops the state machine delayed workqueue, sets the 120662306a36Sopenharmony_ci * state to UP (unless it wasn't up yet). This function must be 120762306a36Sopenharmony_ci * called BEFORE phy_detach. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_civoid phy_stop_machine(struct phy_device *phydev) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci cancel_delayed_work_sync(&phydev->state_queue); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci mutex_lock(&phydev->lock); 121462306a36Sopenharmony_ci if (phy_is_started(phydev)) 121562306a36Sopenharmony_ci phydev->state = PHY_UP; 121662306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic void phy_process_error(struct phy_device *phydev) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci /* phydev->lock must be held for the state change to be safe */ 122262306a36Sopenharmony_ci if (!mutex_is_locked(&phydev->lock)) 122362306a36Sopenharmony_ci phydev_err(phydev, "PHY-device data unsafe context\n"); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci phydev->state = PHY_ERROR; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci phy_trigger_machine(phydev); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic void phy_error_precise(struct phy_device *phydev, 123162306a36Sopenharmony_ci const void *func, int err) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci WARN(1, "%pS: returned: %d\n", func, err); 123462306a36Sopenharmony_ci mutex_lock(&phydev->lock); 123562306a36Sopenharmony_ci phy_process_error(phydev); 123662306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci/** 124062306a36Sopenharmony_ci * phy_error - enter ERROR state for this PHY device 124162306a36Sopenharmony_ci * @phydev: target phy_device struct 124262306a36Sopenharmony_ci * 124362306a36Sopenharmony_ci * Moves the PHY to the ERROR state in response to a read 124462306a36Sopenharmony_ci * or write error, and tells the controller the link is down. 124562306a36Sopenharmony_ci * Must be called with phydev->lock held. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_civoid phy_error(struct phy_device *phydev) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci WARN_ON(1); 125062306a36Sopenharmony_ci phy_process_error(phydev); 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ciEXPORT_SYMBOL(phy_error); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci/** 125562306a36Sopenharmony_ci * phy_disable_interrupts - Disable the PHY interrupts from the PHY side 125662306a36Sopenharmony_ci * @phydev: target phy_device struct 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ciint phy_disable_interrupts(struct phy_device *phydev) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci /* Disable PHY interrupts */ 126162306a36Sopenharmony_ci return phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci/** 126562306a36Sopenharmony_ci * phy_interrupt - PHY interrupt handler 126662306a36Sopenharmony_ci * @irq: interrupt line 126762306a36Sopenharmony_ci * @phy_dat: phy_device pointer 126862306a36Sopenharmony_ci * 126962306a36Sopenharmony_ci * Description: Handle PHY interrupt 127062306a36Sopenharmony_ci */ 127162306a36Sopenharmony_cistatic irqreturn_t phy_interrupt(int irq, void *phy_dat) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci struct phy_device *phydev = phy_dat; 127462306a36Sopenharmony_ci struct phy_driver *drv = phydev->drv; 127562306a36Sopenharmony_ci irqreturn_t ret; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci /* Wakeup interrupts may occur during a system sleep transition. 127862306a36Sopenharmony_ci * Postpone handling until the PHY has resumed. 127962306a36Sopenharmony_ci */ 128062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PM_SLEEP) && phydev->irq_suspended) { 128162306a36Sopenharmony_ci struct net_device *netdev = phydev->attached_dev; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci if (netdev) { 128462306a36Sopenharmony_ci struct device *parent = netdev->dev.parent; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (netdev->wol_enabled) 128762306a36Sopenharmony_ci pm_system_wakeup(); 128862306a36Sopenharmony_ci else if (device_may_wakeup(&netdev->dev)) 128962306a36Sopenharmony_ci pm_wakeup_dev_event(&netdev->dev, 0, true); 129062306a36Sopenharmony_ci else if (parent && device_may_wakeup(parent)) 129162306a36Sopenharmony_ci pm_wakeup_dev_event(parent, 0, true); 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci phydev->irq_rerun = 1; 129562306a36Sopenharmony_ci disable_irq_nosync(irq); 129662306a36Sopenharmony_ci return IRQ_HANDLED; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci mutex_lock(&phydev->lock); 130062306a36Sopenharmony_ci ret = drv->handle_interrupt(phydev); 130162306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci return ret; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci/** 130762306a36Sopenharmony_ci * phy_enable_interrupts - Enable the interrupts from the PHY side 130862306a36Sopenharmony_ci * @phydev: target phy_device struct 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_cistatic int phy_enable_interrupts(struct phy_device *phydev) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci return phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci/** 131662306a36Sopenharmony_ci * phy_request_interrupt - request and enable interrupt for a PHY device 131762306a36Sopenharmony_ci * @phydev: target phy_device struct 131862306a36Sopenharmony_ci * 131962306a36Sopenharmony_ci * Description: Request and enable the interrupt for the given PHY. 132062306a36Sopenharmony_ci * If this fails, then we set irq to PHY_POLL. 132162306a36Sopenharmony_ci * This should only be called with a valid IRQ number. 132262306a36Sopenharmony_ci */ 132362306a36Sopenharmony_civoid phy_request_interrupt(struct phy_device *phydev) 132462306a36Sopenharmony_ci{ 132562306a36Sopenharmony_ci int err; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci err = request_threaded_irq(phydev->irq, NULL, phy_interrupt, 132862306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, 132962306a36Sopenharmony_ci phydev_name(phydev), phydev); 133062306a36Sopenharmony_ci if (err) { 133162306a36Sopenharmony_ci phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n", 133262306a36Sopenharmony_ci err, phydev->irq); 133362306a36Sopenharmony_ci phydev->irq = PHY_POLL; 133462306a36Sopenharmony_ci } else { 133562306a36Sopenharmony_ci if (phy_enable_interrupts(phydev)) { 133662306a36Sopenharmony_ci phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n"); 133762306a36Sopenharmony_ci phy_free_interrupt(phydev); 133862306a36Sopenharmony_ci phydev->irq = PHY_POLL; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ciEXPORT_SYMBOL(phy_request_interrupt); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci/** 134562306a36Sopenharmony_ci * phy_free_interrupt - disable and free interrupt for a PHY device 134662306a36Sopenharmony_ci * @phydev: target phy_device struct 134762306a36Sopenharmony_ci * 134862306a36Sopenharmony_ci * Description: Disable and free the interrupt for the given PHY. 134962306a36Sopenharmony_ci * This should only be called with a valid IRQ number. 135062306a36Sopenharmony_ci */ 135162306a36Sopenharmony_civoid phy_free_interrupt(struct phy_device *phydev) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci phy_disable_interrupts(phydev); 135462306a36Sopenharmony_ci free_irq(phydev->irq, phydev); 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ciEXPORT_SYMBOL(phy_free_interrupt); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/** 135962306a36Sopenharmony_ci * phy_stop - Bring down the PHY link, and stop checking the status 136062306a36Sopenharmony_ci * @phydev: target phy_device struct 136162306a36Sopenharmony_ci */ 136262306a36Sopenharmony_civoid phy_stop(struct phy_device *phydev) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 136562306a36Sopenharmony_ci enum phy_state old_state; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci if (!phy_is_started(phydev) && phydev->state != PHY_DOWN && 136862306a36Sopenharmony_ci phydev->state != PHY_ERROR) { 136962306a36Sopenharmony_ci WARN(1, "called from state %s\n", 137062306a36Sopenharmony_ci phy_state_to_str(phydev->state)); 137162306a36Sopenharmony_ci return; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci mutex_lock(&phydev->lock); 137562306a36Sopenharmony_ci old_state = phydev->state; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (phydev->state == PHY_CABLETEST) { 137862306a36Sopenharmony_ci phy_abort_cable_test(phydev); 137962306a36Sopenharmony_ci netif_testing_off(dev); 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci if (phydev->sfp_bus) 138362306a36Sopenharmony_ci sfp_upstream_stop(phydev->sfp_bus); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci phydev->state = PHY_HALTED; 138662306a36Sopenharmony_ci phy_process_state_change(phydev, old_state); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci phy_state_machine(&phydev->state_queue.work); 139162306a36Sopenharmony_ci phy_stop_machine(phydev); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci /* Cannot call flush_scheduled_work() here as desired because 139462306a36Sopenharmony_ci * of rtnl_lock(), but PHY_HALTED shall guarantee irq handler 139562306a36Sopenharmony_ci * will not reenable interrupts. 139662306a36Sopenharmony_ci */ 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ciEXPORT_SYMBOL(phy_stop); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci/** 140162306a36Sopenharmony_ci * phy_start - start or restart a PHY device 140262306a36Sopenharmony_ci * @phydev: target phy_device struct 140362306a36Sopenharmony_ci * 140462306a36Sopenharmony_ci * Description: Indicates the attached device's readiness to 140562306a36Sopenharmony_ci * handle PHY-related work. Used during startup to start the 140662306a36Sopenharmony_ci * PHY, and after a call to phy_stop() to resume operation. 140762306a36Sopenharmony_ci * Also used to indicate the MDIO bus has cleared an error 140862306a36Sopenharmony_ci * condition. 140962306a36Sopenharmony_ci */ 141062306a36Sopenharmony_civoid phy_start(struct phy_device *phydev) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci mutex_lock(&phydev->lock); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (phydev->state != PHY_READY && phydev->state != PHY_HALTED) { 141562306a36Sopenharmony_ci WARN(1, "called from state %s\n", 141662306a36Sopenharmony_ci phy_state_to_str(phydev->state)); 141762306a36Sopenharmony_ci goto out; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (phydev->sfp_bus) 142162306a36Sopenharmony_ci sfp_upstream_start(phydev->sfp_bus); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci /* if phy was suspended, bring the physical link up again */ 142462306a36Sopenharmony_ci __phy_resume(phydev); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci phydev->state = PHY_UP; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci phy_start_machine(phydev); 142962306a36Sopenharmony_ciout: 143062306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ciEXPORT_SYMBOL(phy_start); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci/** 143562306a36Sopenharmony_ci * phy_state_machine - Handle the state machine 143662306a36Sopenharmony_ci * @work: work_struct that describes the work to be done 143762306a36Sopenharmony_ci */ 143862306a36Sopenharmony_civoid phy_state_machine(struct work_struct *work) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 144162306a36Sopenharmony_ci struct phy_device *phydev = 144262306a36Sopenharmony_ci container_of(dwork, struct phy_device, state_queue); 144362306a36Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 144462306a36Sopenharmony_ci bool needs_aneg = false, do_suspend = false; 144562306a36Sopenharmony_ci enum phy_state old_state; 144662306a36Sopenharmony_ci const void *func = NULL; 144762306a36Sopenharmony_ci bool finished = false; 144862306a36Sopenharmony_ci int err = 0; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci mutex_lock(&phydev->lock); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci old_state = phydev->state; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci switch (phydev->state) { 145562306a36Sopenharmony_ci case PHY_DOWN: 145662306a36Sopenharmony_ci case PHY_READY: 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci case PHY_UP: 145962306a36Sopenharmony_ci needs_aneg = true; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci break; 146262306a36Sopenharmony_ci case PHY_NOLINK: 146362306a36Sopenharmony_ci case PHY_RUNNING: 146462306a36Sopenharmony_ci err = phy_check_link_status(phydev); 146562306a36Sopenharmony_ci func = &phy_check_link_status; 146662306a36Sopenharmony_ci break; 146762306a36Sopenharmony_ci case PHY_CABLETEST: 146862306a36Sopenharmony_ci err = phydev->drv->cable_test_get_status(phydev, &finished); 146962306a36Sopenharmony_ci if (err) { 147062306a36Sopenharmony_ci phy_abort_cable_test(phydev); 147162306a36Sopenharmony_ci netif_testing_off(dev); 147262306a36Sopenharmony_ci needs_aneg = true; 147362306a36Sopenharmony_ci phydev->state = PHY_UP; 147462306a36Sopenharmony_ci break; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (finished) { 147862306a36Sopenharmony_ci ethnl_cable_test_finished(phydev); 147962306a36Sopenharmony_ci netif_testing_off(dev); 148062306a36Sopenharmony_ci needs_aneg = true; 148162306a36Sopenharmony_ci phydev->state = PHY_UP; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci break; 148462306a36Sopenharmony_ci case PHY_HALTED: 148562306a36Sopenharmony_ci case PHY_ERROR: 148662306a36Sopenharmony_ci if (phydev->link) { 148762306a36Sopenharmony_ci phydev->link = 0; 148862306a36Sopenharmony_ci phy_link_down(phydev); 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci do_suspend = true; 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (needs_aneg) { 149762306a36Sopenharmony_ci err = phy_start_aneg(phydev); 149862306a36Sopenharmony_ci func = &phy_start_aneg; 149962306a36Sopenharmony_ci } else if (do_suspend) { 150062306a36Sopenharmony_ci phy_suspend(phydev); 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (err == -ENODEV) 150462306a36Sopenharmony_ci return; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (err < 0) 150762306a36Sopenharmony_ci phy_error_precise(phydev, func, err); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci phy_process_state_change(phydev, old_state); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* Only re-schedule a PHY state machine change if we are polling the 151262306a36Sopenharmony_ci * PHY, if PHY_MAC_INTERRUPT is set, then we will be moving 151362306a36Sopenharmony_ci * between states from phy_mac_interrupt(). 151462306a36Sopenharmony_ci * 151562306a36Sopenharmony_ci * In state PHY_HALTED the PHY gets suspended, so rescheduling the 151662306a36Sopenharmony_ci * state machine would be pointless and possibly error prone when 151762306a36Sopenharmony_ci * called from phy_disconnect() synchronously. 151862306a36Sopenharmony_ci */ 151962306a36Sopenharmony_ci mutex_lock(&phydev->lock); 152062306a36Sopenharmony_ci if (phy_polling_mode(phydev) && phy_is_started(phydev)) 152162306a36Sopenharmony_ci phy_queue_state_machine(phydev, PHY_STATE_TIME); 152262306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci/** 152662306a36Sopenharmony_ci * phy_mac_interrupt - MAC says the link has changed 152762306a36Sopenharmony_ci * @phydev: phy_device struct with changed link 152862306a36Sopenharmony_ci * 152962306a36Sopenharmony_ci * The MAC layer is able to indicate there has been a change in the PHY link 153062306a36Sopenharmony_ci * status. Trigger the state machine and work a work queue. 153162306a36Sopenharmony_ci */ 153262306a36Sopenharmony_civoid phy_mac_interrupt(struct phy_device *phydev) 153362306a36Sopenharmony_ci{ 153462306a36Sopenharmony_ci /* Trigger a state machine change */ 153562306a36Sopenharmony_ci phy_trigger_machine(phydev); 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ciEXPORT_SYMBOL(phy_mac_interrupt); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci/** 154062306a36Sopenharmony_ci * phy_init_eee - init and check the EEE feature 154162306a36Sopenharmony_ci * @phydev: target phy_device struct 154262306a36Sopenharmony_ci * @clk_stop_enable: PHY may stop the clock during LPI 154362306a36Sopenharmony_ci * 154462306a36Sopenharmony_ci * Description: it checks if the Energy-Efficient Ethernet (EEE) 154562306a36Sopenharmony_ci * is supported by looking at the MMD registers 3.20 and 7.60/61 154662306a36Sopenharmony_ci * and it programs the MMD register 3.0 setting the "Clock stop enable" 154762306a36Sopenharmony_ci * bit if required. 154862306a36Sopenharmony_ci */ 154962306a36Sopenharmony_ciint phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci int ret; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (!phydev->drv) 155462306a36Sopenharmony_ci return -EIO; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci ret = genphy_c45_eee_is_active(phydev, NULL, NULL, NULL); 155762306a36Sopenharmony_ci if (ret < 0) 155862306a36Sopenharmony_ci return ret; 155962306a36Sopenharmony_ci if (!ret) 156062306a36Sopenharmony_ci return -EPROTONOSUPPORT; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (clk_stop_enable) 156362306a36Sopenharmony_ci /* Configure the PHY to stop receiving xMII 156462306a36Sopenharmony_ci * clock while it is signaling LPI. 156562306a36Sopenharmony_ci */ 156662306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, 156762306a36Sopenharmony_ci MDIO_PCS_CTRL1_CLKSTOP_EN); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci return ret < 0 ? ret : 0; 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ciEXPORT_SYMBOL(phy_init_eee); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci/** 157462306a36Sopenharmony_ci * phy_get_eee_err - report the EEE wake error count 157562306a36Sopenharmony_ci * @phydev: target phy_device struct 157662306a36Sopenharmony_ci * 157762306a36Sopenharmony_ci * Description: it is to report the number of time where the PHY 157862306a36Sopenharmony_ci * failed to complete its normal wake sequence. 157962306a36Sopenharmony_ci */ 158062306a36Sopenharmony_ciint phy_get_eee_err(struct phy_device *phydev) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci int ret; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (!phydev->drv) 158562306a36Sopenharmony_ci return -EIO; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci mutex_lock(&phydev->lock); 158862306a36Sopenharmony_ci ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR); 158962306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci return ret; 159262306a36Sopenharmony_ci} 159362306a36Sopenharmony_ciEXPORT_SYMBOL(phy_get_eee_err); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci/** 159662306a36Sopenharmony_ci * phy_ethtool_get_eee - get EEE supported and status 159762306a36Sopenharmony_ci * @phydev: target phy_device struct 159862306a36Sopenharmony_ci * @data: ethtool_eee data 159962306a36Sopenharmony_ci * 160062306a36Sopenharmony_ci * Description: it reportes the Supported/Advertisement/LP Advertisement 160162306a36Sopenharmony_ci * capabilities. 160262306a36Sopenharmony_ci */ 160362306a36Sopenharmony_ciint phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) 160462306a36Sopenharmony_ci{ 160562306a36Sopenharmony_ci int ret; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (!phydev->drv) 160862306a36Sopenharmony_ci return -EIO; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci mutex_lock(&phydev->lock); 161162306a36Sopenharmony_ci ret = genphy_c45_ethtool_get_eee(phydev, data); 161262306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci return ret; 161562306a36Sopenharmony_ci} 161662306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_eee); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci/** 161962306a36Sopenharmony_ci * phy_ethtool_set_eee - set EEE supported and status 162062306a36Sopenharmony_ci * @phydev: target phy_device struct 162162306a36Sopenharmony_ci * @data: ethtool_eee data 162262306a36Sopenharmony_ci * 162362306a36Sopenharmony_ci * Description: it is to program the Advertisement EEE register. 162462306a36Sopenharmony_ci */ 162562306a36Sopenharmony_ciint phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci int ret; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (!phydev->drv) 163062306a36Sopenharmony_ci return -EIO; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci mutex_lock(&phydev->lock); 163362306a36Sopenharmony_ci ret = genphy_c45_ethtool_set_eee(phydev, data); 163462306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return ret; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_set_eee); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci/** 164162306a36Sopenharmony_ci * phy_ethtool_set_wol - Configure Wake On LAN 164262306a36Sopenharmony_ci * 164362306a36Sopenharmony_ci * @phydev: target phy_device struct 164462306a36Sopenharmony_ci * @wol: Configuration requested 164562306a36Sopenharmony_ci */ 164662306a36Sopenharmony_ciint phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci int ret; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (phydev->drv && phydev->drv->set_wol) { 165162306a36Sopenharmony_ci mutex_lock(&phydev->lock); 165262306a36Sopenharmony_ci ret = phydev->drv->set_wol(phydev, wol); 165362306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci return ret; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci return -EOPNOTSUPP; 165962306a36Sopenharmony_ci} 166062306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_set_wol); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci/** 166362306a36Sopenharmony_ci * phy_ethtool_get_wol - Get the current Wake On LAN configuration 166462306a36Sopenharmony_ci * 166562306a36Sopenharmony_ci * @phydev: target phy_device struct 166662306a36Sopenharmony_ci * @wol: Store the current configuration here 166762306a36Sopenharmony_ci */ 166862306a36Sopenharmony_civoid phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci if (phydev->drv && phydev->drv->get_wol) { 167162306a36Sopenharmony_ci mutex_lock(&phydev->lock); 167262306a36Sopenharmony_ci phydev->drv->get_wol(phydev, wol); 167362306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_wol); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ciint phy_ethtool_get_link_ksettings(struct net_device *ndev, 167962306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (!phydev) 168462306a36Sopenharmony_ci return -ENODEV; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci phy_ethtool_ksettings_get(phydev, cmd); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci return 0; 168962306a36Sopenharmony_ci} 169062306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_link_ksettings); 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ciint phy_ethtool_set_link_ksettings(struct net_device *ndev, 169362306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci if (!phydev) 169862306a36Sopenharmony_ci return -ENODEV; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci return phy_ethtool_ksettings_set(phydev, cmd); 170162306a36Sopenharmony_ci} 170262306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_set_link_ksettings); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci/** 170562306a36Sopenharmony_ci * phy_ethtool_nway_reset - Restart auto negotiation 170662306a36Sopenharmony_ci * @ndev: Network device to restart autoneg for 170762306a36Sopenharmony_ci */ 170862306a36Sopenharmony_ciint phy_ethtool_nway_reset(struct net_device *ndev) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 171162306a36Sopenharmony_ci int ret; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (!phydev) 171462306a36Sopenharmony_ci return -ENODEV; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (!phydev->drv) 171762306a36Sopenharmony_ci return -EIO; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci mutex_lock(&phydev->lock); 172062306a36Sopenharmony_ci ret = phy_restart_aneg(phydev); 172162306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci return ret; 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_nway_reset); 1726