18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* Framework for configuring and reading PHY devices 38c2ecf20Sopenharmony_ci * Based on code in sungem_phy.c and gianfar_phy.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Andy Fleming 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2004 Freescale Semiconductor, Inc. 88c2ecf20Sopenharmony_ci * Copyright (c) 2006, 2007 Maciej W. Rozycki 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/unistd.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/netlink.h> 198c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 218c2ecf20Sopenharmony_ci#include <linux/mm.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/mii.h> 248c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 258c2ecf20Sopenharmony_ci#include <linux/ethtool_netlink.h> 268c2ecf20Sopenharmony_ci#include <linux/phy.h> 278c2ecf20Sopenharmony_ci#include <linux/phy_led_triggers.h> 288c2ecf20Sopenharmony_ci#include <linux/sfp.h> 298c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 308c2ecf20Sopenharmony_ci#include <linux/mdio.h> 318c2ecf20Sopenharmony_ci#include <linux/io.h> 328c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 338c2ecf20Sopenharmony_ci#include <linux/atomic.h> 348c2ecf20Sopenharmony_ci#include <net/netlink.h> 358c2ecf20Sopenharmony_ci#include <net/genetlink.h> 368c2ecf20Sopenharmony_ci#include <net/sock.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define PHY_STATE_TIME HZ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define PHY_STATE_STR(_state) \ 418c2ecf20Sopenharmony_ci case PHY_##_state: \ 428c2ecf20Sopenharmony_ci return __stringify(_state); \ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const char *phy_state_to_str(enum phy_state st) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci switch (st) { 478c2ecf20Sopenharmony_ci PHY_STATE_STR(DOWN) 488c2ecf20Sopenharmony_ci PHY_STATE_STR(READY) 498c2ecf20Sopenharmony_ci PHY_STATE_STR(UP) 508c2ecf20Sopenharmony_ci PHY_STATE_STR(RUNNING) 518c2ecf20Sopenharmony_ci PHY_STATE_STR(NOLINK) 528c2ecf20Sopenharmony_ci PHY_STATE_STR(CABLETEST) 538c2ecf20Sopenharmony_ci PHY_STATE_STR(HALTED) 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return NULL; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void phy_process_state_change(struct phy_device *phydev, 608c2ecf20Sopenharmony_ci enum phy_state old_state) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci if (old_state != phydev->state) { 638c2ecf20Sopenharmony_ci phydev_dbg(phydev, "PHY state change %s -> %s\n", 648c2ecf20Sopenharmony_ci phy_state_to_str(old_state), 658c2ecf20Sopenharmony_ci phy_state_to_str(phydev->state)); 668c2ecf20Sopenharmony_ci if (phydev->drv && phydev->drv->link_change_notify) 678c2ecf20Sopenharmony_ci phydev->drv->link_change_notify(phydev); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void phy_link_up(struct phy_device *phydev) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci phydev->phy_link_change(phydev, true); 748c2ecf20Sopenharmony_ci phy_led_trigger_change_speed(phydev); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void phy_link_down(struct phy_device *phydev) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci phydev->phy_link_change(phydev, false); 808c2ecf20Sopenharmony_ci phy_led_trigger_change_speed(phydev); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const char *phy_pause_str(struct phy_device *phydev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci bool local_pause, local_asym_pause; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (phydev->autoneg == AUTONEG_DISABLE) 888c2ecf20Sopenharmony_ci goto no_pause; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci local_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, 918c2ecf20Sopenharmony_ci phydev->advertising); 928c2ecf20Sopenharmony_ci local_asym_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 938c2ecf20Sopenharmony_ci phydev->advertising); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (local_pause && phydev->pause) 968c2ecf20Sopenharmony_ci return "rx/tx"; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (local_asym_pause && phydev->asym_pause) { 998c2ecf20Sopenharmony_ci if (local_pause) 1008c2ecf20Sopenharmony_ci return "rx"; 1018c2ecf20Sopenharmony_ci if (phydev->pause) 1028c2ecf20Sopenharmony_ci return "tx"; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cino_pause: 1068c2ecf20Sopenharmony_ci return "off"; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * phy_print_status - Convenience function to print out the current phy status 1118c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_civoid phy_print_status(struct phy_device *phydev) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (phydev->link) { 1168c2ecf20Sopenharmony_ci netdev_info(phydev->attached_dev, 1178c2ecf20Sopenharmony_ci "Link is Up - %s/%s %s- flow control %s\n", 1188c2ecf20Sopenharmony_ci phy_speed_to_str(phydev->speed), 1198c2ecf20Sopenharmony_ci phy_duplex_to_str(phydev->duplex), 1208c2ecf20Sopenharmony_ci phydev->downshifted_rate ? "(downshifted) " : "", 1218c2ecf20Sopenharmony_ci phy_pause_str(phydev)); 1228c2ecf20Sopenharmony_ci } else { 1238c2ecf20Sopenharmony_ci netdev_info(phydev->attached_dev, "Link is Down\n"); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_print_status); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/** 1298c2ecf20Sopenharmony_ci * phy_clear_interrupt - Ack the phy device's interrupt 1308c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * If the @phydev driver has an ack_interrupt function, call it to 1338c2ecf20Sopenharmony_ci * ack and clear the phy device's interrupt. 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * Returns 0 on success or < 0 on error. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_cistatic int phy_clear_interrupt(struct phy_device *phydev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci int ret = 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (phydev->drv->ack_interrupt) { 1428c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 1438c2ecf20Sopenharmony_ci ret = phydev->drv->ack_interrupt(phydev); 1448c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * phy_config_interrupt - configure the PHY device for the requested interrupts 1528c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 1538c2ecf20Sopenharmony_ci * @interrupts: interrupt flags to configure for this @phydev 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * Returns 0 on success or < 0 on error. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic int phy_config_interrupt(struct phy_device *phydev, bool interrupts) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci phydev->interrupts = interrupts ? 1 : 0; 1608c2ecf20Sopenharmony_ci if (phydev->drv->config_intr) 1618c2ecf20Sopenharmony_ci return phydev->drv->config_intr(phydev); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/** 1678c2ecf20Sopenharmony_ci * phy_restart_aneg - restart auto-negotiation 1688c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Restart the autonegotiation on @phydev. Returns >= 0 on success or 1718c2ecf20Sopenharmony_ci * negative errno on error. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ciint phy_restart_aneg(struct phy_device *phydev) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) 1788c2ecf20Sopenharmony_ci ret = genphy_c45_restart_aneg(phydev); 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci ret = genphy_restart_aneg(phydev); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_restart_aneg); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/** 1878c2ecf20Sopenharmony_ci * phy_aneg_done - return auto-negotiation status 1888c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * Description: Return the auto-negotiation status from this @phydev 1918c2ecf20Sopenharmony_ci * Returns > 0 on success or < 0 on error. 0 means that auto-negotiation 1928c2ecf20Sopenharmony_ci * is still pending. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ciint phy_aneg_done(struct phy_device *phydev) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci if (phydev->drv && phydev->drv->aneg_done) 1978c2ecf20Sopenharmony_ci return phydev->drv->aneg_done(phydev); 1988c2ecf20Sopenharmony_ci else if (phydev->is_c45) 1998c2ecf20Sopenharmony_ci return genphy_c45_aneg_done(phydev); 2008c2ecf20Sopenharmony_ci else 2018c2ecf20Sopenharmony_ci return genphy_aneg_done(phydev); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_aneg_done); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/** 2068c2ecf20Sopenharmony_ci * phy_find_valid - find a PHY setting that matches the requested parameters 2078c2ecf20Sopenharmony_ci * @speed: desired speed 2088c2ecf20Sopenharmony_ci * @duplex: desired duplex 2098c2ecf20Sopenharmony_ci * @supported: mask of supported link modes 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Locate a supported phy setting that is, in priority order: 2128c2ecf20Sopenharmony_ci * - an exact match for the specified speed and duplex mode 2138c2ecf20Sopenharmony_ci * - a match for the specified speed, or slower speed 2148c2ecf20Sopenharmony_ci * - the slowest supported speed 2158c2ecf20Sopenharmony_ci * Returns the matched phy_setting entry, or %NULL if no supported phy 2168c2ecf20Sopenharmony_ci * settings were found. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic const struct phy_setting * 2198c2ecf20Sopenharmony_ciphy_find_valid(int speed, int duplex, unsigned long *supported) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci return phy_lookup_setting(speed, duplex, supported, false); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * phy_supported_speeds - return all speeds currently supported by a phy device 2268c2ecf20Sopenharmony_ci * @phy: The phy device to return supported speeds of. 2278c2ecf20Sopenharmony_ci * @speeds: buffer to store supported speeds in. 2288c2ecf20Sopenharmony_ci * @size: size of speeds buffer. 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * Description: Returns the number of supported speeds, and fills the speeds 2318c2ecf20Sopenharmony_ci * buffer with the supported speeds. If speeds buffer is too small to contain 2328c2ecf20Sopenharmony_ci * all currently supported speeds, will return as many speeds as can fit. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ciunsigned int phy_supported_speeds(struct phy_device *phy, 2358c2ecf20Sopenharmony_ci unsigned int *speeds, 2368c2ecf20Sopenharmony_ci unsigned int size) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci return phy_speeds(speeds, size, phy->supported); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/** 2428c2ecf20Sopenharmony_ci * phy_check_valid - check if there is a valid PHY setting which matches 2438c2ecf20Sopenharmony_ci * speed, duplex, and feature mask 2448c2ecf20Sopenharmony_ci * @speed: speed to match 2458c2ecf20Sopenharmony_ci * @duplex: duplex to match 2468c2ecf20Sopenharmony_ci * @features: A mask of the valid settings 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Description: Returns true if there is a valid setting, false otherwise. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic inline bool phy_check_valid(int speed, int duplex, 2518c2ecf20Sopenharmony_ci unsigned long *features) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci return !!phy_lookup_setting(speed, duplex, features, true); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex 2588c2ecf20Sopenharmony_ci * @phydev: the target phy_device struct 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * Description: Make sure the PHY is set to supported speeds and 2618c2ecf20Sopenharmony_ci * duplexes. Drop down by one in this order: 1000/FULL, 2628c2ecf20Sopenharmony_ci * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_cistatic void phy_sanitize_settings(struct phy_device *phydev) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci const struct phy_setting *setting; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci setting = phy_find_valid(phydev->speed, phydev->duplex, 2698c2ecf20Sopenharmony_ci phydev->supported); 2708c2ecf20Sopenharmony_ci if (setting) { 2718c2ecf20Sopenharmony_ci phydev->speed = setting->speed; 2728c2ecf20Sopenharmony_ci phydev->duplex = setting->duplex; 2738c2ecf20Sopenharmony_ci } else { 2748c2ecf20Sopenharmony_ci /* We failed to find anything (no supported speeds?) */ 2758c2ecf20Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 2768c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_civoid phy_ethtool_ksettings_get(struct phy_device *phydev, 2818c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 2848c2ecf20Sopenharmony_ci linkmode_copy(cmd->link_modes.supported, phydev->supported); 2858c2ecf20Sopenharmony_ci linkmode_copy(cmd->link_modes.advertising, phydev->advertising); 2868c2ecf20Sopenharmony_ci linkmode_copy(cmd->link_modes.lp_advertising, phydev->lp_advertising); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci cmd->base.speed = phydev->speed; 2898c2ecf20Sopenharmony_ci cmd->base.duplex = phydev->duplex; 2908c2ecf20Sopenharmony_ci cmd->base.master_slave_cfg = phydev->master_slave_get; 2918c2ecf20Sopenharmony_ci cmd->base.master_slave_state = phydev->master_slave_state; 2928c2ecf20Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_MOCA) 2938c2ecf20Sopenharmony_ci cmd->base.port = PORT_BNC; 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci cmd->base.port = phydev->port; 2968c2ecf20Sopenharmony_ci cmd->base.transceiver = phy_is_internal(phydev) ? 2978c2ecf20Sopenharmony_ci XCVR_INTERNAL : XCVR_EXTERNAL; 2988c2ecf20Sopenharmony_ci cmd->base.phy_address = phydev->mdio.addr; 2998c2ecf20Sopenharmony_ci cmd->base.autoneg = phydev->autoneg; 3008c2ecf20Sopenharmony_ci cmd->base.eth_tp_mdix_ctrl = phydev->mdix_ctrl; 3018c2ecf20Sopenharmony_ci cmd->base.eth_tp_mdix = phydev->mdix; 3028c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_ksettings_get); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/** 3078c2ecf20Sopenharmony_ci * phy_mii_ioctl - generic PHY MII ioctl interface 3088c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 3098c2ecf20Sopenharmony_ci * @ifr: &struct ifreq for socket ioctl's 3108c2ecf20Sopenharmony_ci * @cmd: ioctl cmd to execute 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * Note that this function is currently incompatible with the 3138c2ecf20Sopenharmony_ci * PHYCONTROL layer. It changes registers without regard to 3148c2ecf20Sopenharmony_ci * current state. Use at own risk. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ciint phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct mii_ioctl_data *mii_data = if_mii(ifr); 3198c2ecf20Sopenharmony_ci u16 val = mii_data->val_in; 3208c2ecf20Sopenharmony_ci bool change_autoneg = false; 3218c2ecf20Sopenharmony_ci int prtad, devad; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci switch (cmd) { 3248c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 3258c2ecf20Sopenharmony_ci mii_data->phy_id = phydev->mdio.addr; 3268c2ecf20Sopenharmony_ci fallthrough; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci case SIOCGMIIREG: 3298c2ecf20Sopenharmony_ci if (mdio_phy_id_is_c45(mii_data->phy_id)) { 3308c2ecf20Sopenharmony_ci prtad = mdio_phy_id_prtad(mii_data->phy_id); 3318c2ecf20Sopenharmony_ci devad = mdio_phy_id_devad(mii_data->phy_id); 3328c2ecf20Sopenharmony_ci devad = mdiobus_c45_addr(devad, mii_data->reg_num); 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci prtad = mii_data->phy_id; 3358c2ecf20Sopenharmony_ci devad = mii_data->reg_num; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci mii_data->val_out = mdiobus_read(phydev->mdio.bus, prtad, 3388c2ecf20Sopenharmony_ci devad); 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci case SIOCSMIIREG: 3428c2ecf20Sopenharmony_ci if (mdio_phy_id_is_c45(mii_data->phy_id)) { 3438c2ecf20Sopenharmony_ci prtad = mdio_phy_id_prtad(mii_data->phy_id); 3448c2ecf20Sopenharmony_ci devad = mdio_phy_id_devad(mii_data->phy_id); 3458c2ecf20Sopenharmony_ci devad = mdiobus_c45_addr(devad, mii_data->reg_num); 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci prtad = mii_data->phy_id; 3488c2ecf20Sopenharmony_ci devad = mii_data->reg_num; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci if (prtad == phydev->mdio.addr) { 3518c2ecf20Sopenharmony_ci switch (devad) { 3528c2ecf20Sopenharmony_ci case MII_BMCR: 3538c2ecf20Sopenharmony_ci if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) { 3548c2ecf20Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE) 3558c2ecf20Sopenharmony_ci change_autoneg = true; 3568c2ecf20Sopenharmony_ci phydev->autoneg = AUTONEG_DISABLE; 3578c2ecf20Sopenharmony_ci if (val & BMCR_FULLDPLX) 3588c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_HALF; 3618c2ecf20Sopenharmony_ci if (val & BMCR_SPEED1000) 3628c2ecf20Sopenharmony_ci phydev->speed = SPEED_1000; 3638c2ecf20Sopenharmony_ci else if (val & BMCR_SPEED100) 3648c2ecf20Sopenharmony_ci phydev->speed = SPEED_100; 3658c2ecf20Sopenharmony_ci else phydev->speed = SPEED_10; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci else { 3688c2ecf20Sopenharmony_ci if (phydev->autoneg == AUTONEG_DISABLE) 3698c2ecf20Sopenharmony_ci change_autoneg = true; 3708c2ecf20Sopenharmony_ci phydev->autoneg = AUTONEG_ENABLE; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci case MII_ADVERTISE: 3748c2ecf20Sopenharmony_ci mii_adv_mod_linkmode_adv_t(phydev->advertising, 3758c2ecf20Sopenharmony_ci val); 3768c2ecf20Sopenharmony_ci change_autoneg = true; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case MII_CTRL1000: 3798c2ecf20Sopenharmony_ci mii_ctrl1000_mod_linkmode_adv_t(phydev->advertising, 3808c2ecf20Sopenharmony_ci val); 3818c2ecf20Sopenharmony_ci change_autoneg = true; 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci default: 3848c2ecf20Sopenharmony_ci /* do nothing */ 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci mdiobus_write(phydev->mdio.bus, prtad, devad, val); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (prtad == phydev->mdio.addr && 3928c2ecf20Sopenharmony_ci devad == MII_BMCR && 3938c2ecf20Sopenharmony_ci val & BMCR_RESET) 3948c2ecf20Sopenharmony_ci return phy_init_hw(phydev); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (change_autoneg) 3978c2ecf20Sopenharmony_ci return phy_start_aneg(phydev); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci case SIOCSHWTSTAMP: 4028c2ecf20Sopenharmony_ci if (phydev->mii_ts && phydev->mii_ts->hwtstamp) 4038c2ecf20Sopenharmony_ci return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr); 4048c2ecf20Sopenharmony_ci fallthrough; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci default: 4078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_mii_ioctl); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/** 4138c2ecf20Sopenharmony_ci * phy_do_ioctl - generic ndo_do_ioctl implementation 4148c2ecf20Sopenharmony_ci * @dev: the net_device struct 4158c2ecf20Sopenharmony_ci * @ifr: &struct ifreq for socket ioctl's 4168c2ecf20Sopenharmony_ci * @cmd: ioctl cmd to execute 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ciint phy_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci if (!dev->phydev) 4218c2ecf20Sopenharmony_ci return -ENODEV; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return phy_mii_ioctl(dev->phydev, ifr, cmd); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_do_ioctl); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * phy_do_ioctl_running - generic ndo_do_ioctl implementation but test first 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * @dev: the net_device struct 4318c2ecf20Sopenharmony_ci * @ifr: &struct ifreq for socket ioctl's 4328c2ecf20Sopenharmony_ci * @cmd: ioctl cmd to execute 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * Same as phy_do_ioctl, but ensures that net_device is running before 4358c2ecf20Sopenharmony_ci * handling the ioctl. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ciint phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci if (!netif_running(dev)) 4408c2ecf20Sopenharmony_ci return -ENODEV; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return phy_do_ioctl(dev, ifr, cmd); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_do_ioctl_running); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/** 4478c2ecf20Sopenharmony_ci * phy_queue_state_machine - Trigger the state machine to run soon 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 4508c2ecf20Sopenharmony_ci * @jiffies: Run the state machine after these jiffies 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_civoid phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci mod_delayed_work(system_power_efficient_wq, &phydev->state_queue, 4558c2ecf20Sopenharmony_ci jiffies); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_queue_state_machine); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/** 4608c2ecf20Sopenharmony_ci * phy_queue_state_machine - Trigger the state machine to run now 4618c2ecf20Sopenharmony_ci * 4628c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_cistatic void phy_trigger_machine(struct phy_device *phydev) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci phy_queue_state_machine(phydev, 0); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void phy_abort_cable_test(struct phy_device *phydev) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci int err; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ethnl_cable_test_finished(phydev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci err = phy_init_hw(phydev); 4768c2ecf20Sopenharmony_ci if (err) 4778c2ecf20Sopenharmony_ci phydev_err(phydev, "Error while aborting cable test"); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/** 4818c2ecf20Sopenharmony_ci * phy_ethtool_get_strings - Get the statistic counter names 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 4848c2ecf20Sopenharmony_ci * @data: Where to put the strings 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ciint phy_ethtool_get_strings(struct phy_device *phydev, u8 *data) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci if (!phydev->drv) 4898c2ecf20Sopenharmony_ci return -EIO; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 4928c2ecf20Sopenharmony_ci phydev->drv->get_strings(phydev, data); 4938c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_strings); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/** 5008c2ecf20Sopenharmony_ci * phy_ethtool_get_sset_count - Get the number of statistic counters 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ciint phy_ethtool_get_sset_count(struct phy_device *phydev) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci int ret; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (!phydev->drv) 5098c2ecf20Sopenharmony_ci return -EIO; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (phydev->drv->get_sset_count && 5128c2ecf20Sopenharmony_ci phydev->drv->get_strings && 5138c2ecf20Sopenharmony_ci phydev->drv->get_stats) { 5148c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 5158c2ecf20Sopenharmony_ci ret = phydev->drv->get_sset_count(phydev); 5168c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_sset_count); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/** 5268c2ecf20Sopenharmony_ci * phy_ethtool_get_stats - Get the statistic counters 5278c2ecf20Sopenharmony_ci * 5288c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 5298c2ecf20Sopenharmony_ci * @stats: What counters to get 5308c2ecf20Sopenharmony_ci * @data: Where to store the counters 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ciint phy_ethtool_get_stats(struct phy_device *phydev, 5338c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci if (!phydev->drv) 5368c2ecf20Sopenharmony_ci return -EIO; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 5398c2ecf20Sopenharmony_ci phydev->drv->get_stats(phydev, stats, data); 5408c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_stats); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/** 5478c2ecf20Sopenharmony_ci * phy_start_cable_test - Start a cable test 5488c2ecf20Sopenharmony_ci * 5498c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 5508c2ecf20Sopenharmony_ci * @extack: extack for reporting useful error messages 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ciint phy_start_cable_test(struct phy_device *phydev, 5538c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 5568c2ecf20Sopenharmony_ci int err = -ENOMEM; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!(phydev->drv && 5598c2ecf20Sopenharmony_ci phydev->drv->cable_test_start && 5608c2ecf20Sopenharmony_ci phydev->drv->cable_test_get_status)) { 5618c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 5628c2ecf20Sopenharmony_ci "PHY driver does not support cable testing"); 5638c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 5678c2ecf20Sopenharmony_ci if (phydev->state == PHY_CABLETEST) { 5688c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 5698c2ecf20Sopenharmony_ci "PHY already performing a test"); 5708c2ecf20Sopenharmony_ci err = -EBUSY; 5718c2ecf20Sopenharmony_ci goto out; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (phydev->state < PHY_UP || 5758c2ecf20Sopenharmony_ci phydev->state > PHY_CABLETEST) { 5768c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 5778c2ecf20Sopenharmony_ci "PHY not configured. Try setting interface up"); 5788c2ecf20Sopenharmony_ci err = -EBUSY; 5798c2ecf20Sopenharmony_ci goto out; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_NTF); 5838c2ecf20Sopenharmony_ci if (err) 5848c2ecf20Sopenharmony_ci goto out; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* Mark the carrier down until the test is complete */ 5878c2ecf20Sopenharmony_ci phy_link_down(phydev); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci netif_testing_on(dev); 5908c2ecf20Sopenharmony_ci err = phydev->drv->cable_test_start(phydev); 5918c2ecf20Sopenharmony_ci if (err) { 5928c2ecf20Sopenharmony_ci netif_testing_off(dev); 5938c2ecf20Sopenharmony_ci phy_link_up(phydev); 5948c2ecf20Sopenharmony_ci goto out_free; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci phydev->state = PHY_CABLETEST; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (phy_polling_mode(phydev)) 6008c2ecf20Sopenharmony_ci phy_trigger_machine(phydev); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ciout_free: 6078c2ecf20Sopenharmony_ci ethnl_cable_test_free(phydev); 6088c2ecf20Sopenharmony_ciout: 6098c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return err; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_start_cable_test); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/** 6168c2ecf20Sopenharmony_ci * phy_start_cable_test_tdr - Start a raw TDR cable test 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 6198c2ecf20Sopenharmony_ci * @extack: extack for reporting useful error messages 6208c2ecf20Sopenharmony_ci * @config: Configuration of the test to run 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ciint phy_start_cable_test_tdr(struct phy_device *phydev, 6238c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack, 6248c2ecf20Sopenharmony_ci const struct phy_tdr_config *config) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 6278c2ecf20Sopenharmony_ci int err = -ENOMEM; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!(phydev->drv && 6308c2ecf20Sopenharmony_ci phydev->drv->cable_test_tdr_start && 6318c2ecf20Sopenharmony_ci phydev->drv->cable_test_get_status)) { 6328c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 6338c2ecf20Sopenharmony_ci "PHY driver does not support cable test TDR"); 6348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 6388c2ecf20Sopenharmony_ci if (phydev->state == PHY_CABLETEST) { 6398c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 6408c2ecf20Sopenharmony_ci "PHY already performing a test"); 6418c2ecf20Sopenharmony_ci err = -EBUSY; 6428c2ecf20Sopenharmony_ci goto out; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (phydev->state < PHY_UP || 6468c2ecf20Sopenharmony_ci phydev->state > PHY_CABLETEST) { 6478c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 6488c2ecf20Sopenharmony_ci "PHY not configured. Try setting interface up"); 6498c2ecf20Sopenharmony_ci err = -EBUSY; 6508c2ecf20Sopenharmony_ci goto out; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 6548c2ecf20Sopenharmony_ci if (err) 6558c2ecf20Sopenharmony_ci goto out; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* Mark the carrier down until the test is complete */ 6588c2ecf20Sopenharmony_ci phy_link_down(phydev); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci netif_testing_on(dev); 6618c2ecf20Sopenharmony_ci err = phydev->drv->cable_test_tdr_start(phydev, config); 6628c2ecf20Sopenharmony_ci if (err) { 6638c2ecf20Sopenharmony_ci netif_testing_off(dev); 6648c2ecf20Sopenharmony_ci phy_link_up(phydev); 6658c2ecf20Sopenharmony_ci goto out_free; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci phydev->state = PHY_CABLETEST; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (phy_polling_mode(phydev)) 6718c2ecf20Sopenharmony_ci phy_trigger_machine(phydev); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci return 0; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ciout_free: 6788c2ecf20Sopenharmony_ci ethnl_cable_test_free(phydev); 6798c2ecf20Sopenharmony_ciout: 6808c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci return err; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_start_cable_test_tdr); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int phy_config_aneg(struct phy_device *phydev) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci if (phydev->drv->config_aneg) 6898c2ecf20Sopenharmony_ci return phydev->drv->config_aneg(phydev); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* Clause 45 PHYs that don't implement Clause 22 registers are not 6928c2ecf20Sopenharmony_ci * allowed to call genphy_config_aneg() 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) 6958c2ecf20Sopenharmony_ci return genphy_c45_config_aneg(phydev); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return genphy_config_aneg(phydev); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci/** 7018c2ecf20Sopenharmony_ci * phy_check_link_status - check link status and set state accordingly 7028c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 7038c2ecf20Sopenharmony_ci * 7048c2ecf20Sopenharmony_ci * Description: Check for link and whether autoneg was triggered / is running 7058c2ecf20Sopenharmony_ci * and set state accordingly 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_cistatic int phy_check_link_status(struct phy_device *phydev) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci int err; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&phydev->lock)); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* Keep previous state if loopback is enabled because some PHYs 7148c2ecf20Sopenharmony_ci * report that Link is Down when loopback is enabled. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci if (phydev->loopback_enabled) 7178c2ecf20Sopenharmony_ci return 0; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci err = phy_read_status(phydev); 7208c2ecf20Sopenharmony_ci if (err) 7218c2ecf20Sopenharmony_ci return err; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (phydev->link && phydev->state != PHY_RUNNING) { 7248c2ecf20Sopenharmony_ci phy_check_downshift(phydev); 7258c2ecf20Sopenharmony_ci phydev->state = PHY_RUNNING; 7268c2ecf20Sopenharmony_ci phy_link_up(phydev); 7278c2ecf20Sopenharmony_ci } else if (!phydev->link && phydev->state != PHY_NOLINK) { 7288c2ecf20Sopenharmony_ci phydev->state = PHY_NOLINK; 7298c2ecf20Sopenharmony_ci phy_link_down(phydev); 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci/** 7368c2ecf20Sopenharmony_ci * _phy_start_aneg - start auto-negotiation for this PHY device 7378c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 7388c2ecf20Sopenharmony_ci * 7398c2ecf20Sopenharmony_ci * Description: Sanitizes the settings (if we're not autonegotiating 7408c2ecf20Sopenharmony_ci * them), and then calls the driver's config_aneg function. 7418c2ecf20Sopenharmony_ci * If the PHYCONTROL Layer is operating, we change the state to 7428c2ecf20Sopenharmony_ci * reflect the beginning of Auto-negotiation or forcing. 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_cistatic int _phy_start_aneg(struct phy_device *phydev) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci int err; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci lockdep_assert_held(&phydev->lock); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (!phydev->drv) 7518c2ecf20Sopenharmony_ci return -EIO; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (AUTONEG_DISABLE == phydev->autoneg) 7548c2ecf20Sopenharmony_ci phy_sanitize_settings(phydev); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci err = phy_config_aneg(phydev); 7578c2ecf20Sopenharmony_ci if (err < 0) 7588c2ecf20Sopenharmony_ci return err; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (phy_is_started(phydev)) 7618c2ecf20Sopenharmony_ci err = phy_check_link_status(phydev); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return err; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/** 7678c2ecf20Sopenharmony_ci * phy_start_aneg - start auto-negotiation for this PHY device 7688c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 7698c2ecf20Sopenharmony_ci * 7708c2ecf20Sopenharmony_ci * Description: Sanitizes the settings (if we're not autonegotiating 7718c2ecf20Sopenharmony_ci * them), and then calls the driver's config_aneg function. 7728c2ecf20Sopenharmony_ci * If the PHYCONTROL Layer is operating, we change the state to 7738c2ecf20Sopenharmony_ci * reflect the beginning of Auto-negotiation or forcing. 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_ciint phy_start_aneg(struct phy_device *phydev) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci int err; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 7808c2ecf20Sopenharmony_ci err = _phy_start_aneg(phydev); 7818c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return err; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_start_aneg); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic int phy_poll_aneg_done(struct phy_device *phydev) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci unsigned int retries = 100; 7908c2ecf20Sopenharmony_ci int ret; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci do { 7938c2ecf20Sopenharmony_ci msleep(100); 7948c2ecf20Sopenharmony_ci ret = phy_aneg_done(phydev); 7958c2ecf20Sopenharmony_ci } while (!ret && --retries); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (!ret) 7988c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ciint phy_ethtool_ksettings_set(struct phy_device *phydev, 8048c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); 8078c2ecf20Sopenharmony_ci u8 autoneg = cmd->base.autoneg; 8088c2ecf20Sopenharmony_ci u8 duplex = cmd->base.duplex; 8098c2ecf20Sopenharmony_ci u32 speed = cmd->base.speed; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (cmd->base.phy_address != phydev->mdio.addr) 8128c2ecf20Sopenharmony_ci return -EINVAL; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci linkmode_copy(advertising, cmd->link_modes.advertising); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* We make sure that we don't pass unsupported values in to the PHY */ 8178c2ecf20Sopenharmony_ci linkmode_and(advertising, advertising, phydev->supported); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci /* Verify the settings we care about. */ 8208c2ecf20Sopenharmony_ci if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE) 8218c2ecf20Sopenharmony_ci return -EINVAL; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (autoneg == AUTONEG_ENABLE && linkmode_empty(advertising)) 8248c2ecf20Sopenharmony_ci return -EINVAL; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (autoneg == AUTONEG_DISABLE && 8278c2ecf20Sopenharmony_ci ((speed != SPEED_1000 && 8288c2ecf20Sopenharmony_ci speed != SPEED_100 && 8298c2ecf20Sopenharmony_ci speed != SPEED_10) || 8308c2ecf20Sopenharmony_ci (duplex != DUPLEX_HALF && 8318c2ecf20Sopenharmony_ci duplex != DUPLEX_FULL))) 8328c2ecf20Sopenharmony_ci return -EINVAL; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 8358c2ecf20Sopenharmony_ci phydev->autoneg = autoneg; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (autoneg == AUTONEG_DISABLE) { 8388c2ecf20Sopenharmony_ci phydev->speed = speed; 8398c2ecf20Sopenharmony_ci phydev->duplex = duplex; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci linkmode_copy(phydev->advertising, advertising); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 8458c2ecf20Sopenharmony_ci phydev->advertising, autoneg == AUTONEG_ENABLE); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci phydev->master_slave_set = cmd->base.master_slave_cfg; 8488c2ecf20Sopenharmony_ci phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* Restart the PHY */ 8518c2ecf20Sopenharmony_ci if (phy_is_started(phydev)) { 8528c2ecf20Sopenharmony_ci phydev->state = PHY_UP; 8538c2ecf20Sopenharmony_ci phy_trigger_machine(phydev); 8548c2ecf20Sopenharmony_ci } else { 8558c2ecf20Sopenharmony_ci _phy_start_aneg(phydev); 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 8598c2ecf20Sopenharmony_ci return 0; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_ksettings_set); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci/** 8648c2ecf20Sopenharmony_ci * phy_speed_down - set speed to lowest speed supported by both link partners 8658c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 8668c2ecf20Sopenharmony_ci * @sync: perform action synchronously 8678c2ecf20Sopenharmony_ci * 8688c2ecf20Sopenharmony_ci * Description: Typically used to save energy when waiting for a WoL packet 8698c2ecf20Sopenharmony_ci * 8708c2ecf20Sopenharmony_ci * WARNING: Setting sync to false may cause the system being unable to suspend 8718c2ecf20Sopenharmony_ci * in case the PHY generates an interrupt when finishing the autonegotiation. 8728c2ecf20Sopenharmony_ci * This interrupt may wake up the system immediately after suspend. 8738c2ecf20Sopenharmony_ci * Therefore use sync = false only if you're sure it's safe with the respective 8748c2ecf20Sopenharmony_ci * network chip. 8758c2ecf20Sopenharmony_ci */ 8768c2ecf20Sopenharmony_ciint phy_speed_down(struct phy_device *phydev, bool sync) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_tmp); 8798c2ecf20Sopenharmony_ci int ret; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE) 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci linkmode_copy(adv_tmp, phydev->advertising); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci ret = phy_speed_down_core(phydev); 8878c2ecf20Sopenharmony_ci if (ret) 8888c2ecf20Sopenharmony_ci return ret; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci linkmode_copy(phydev->adv_old, adv_tmp); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (linkmode_equal(phydev->advertising, adv_tmp)) 8938c2ecf20Sopenharmony_ci return 0; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci ret = phy_config_aneg(phydev); 8968c2ecf20Sopenharmony_ci if (ret) 8978c2ecf20Sopenharmony_ci return ret; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return sync ? phy_poll_aneg_done(phydev) : 0; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_speed_down); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci/** 9048c2ecf20Sopenharmony_ci * phy_speed_up - (re)set advertised speeds to all supported speeds 9058c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 9068c2ecf20Sopenharmony_ci * 9078c2ecf20Sopenharmony_ci * Description: Used to revert the effect of phy_speed_down 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_ciint phy_speed_up(struct phy_device *phydev) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_tmp); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE) 9148c2ecf20Sopenharmony_ci return 0; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (linkmode_empty(phydev->adv_old)) 9178c2ecf20Sopenharmony_ci return 0; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci linkmode_copy(adv_tmp, phydev->advertising); 9208c2ecf20Sopenharmony_ci linkmode_copy(phydev->advertising, phydev->adv_old); 9218c2ecf20Sopenharmony_ci linkmode_zero(phydev->adv_old); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (linkmode_equal(phydev->advertising, adv_tmp)) 9248c2ecf20Sopenharmony_ci return 0; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci return phy_config_aneg(phydev); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_speed_up); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci/** 9318c2ecf20Sopenharmony_ci * phy_start_machine - start PHY state machine tracking 9328c2ecf20Sopenharmony_ci * @phydev: the phy_device struct 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci * Description: The PHY infrastructure can run a state machine 9358c2ecf20Sopenharmony_ci * which tracks whether the PHY is starting up, negotiating, 9368c2ecf20Sopenharmony_ci * etc. This function starts the delayed workqueue which tracks 9378c2ecf20Sopenharmony_ci * the state of the PHY. If you want to maintain your own state machine, 9388c2ecf20Sopenharmony_ci * do not call this function. 9398c2ecf20Sopenharmony_ci */ 9408c2ecf20Sopenharmony_civoid phy_start_machine(struct phy_device *phydev) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci phy_trigger_machine(phydev); 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_start_machine); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci/** 9478c2ecf20Sopenharmony_ci * phy_stop_machine - stop the PHY state machine tracking 9488c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 9498c2ecf20Sopenharmony_ci * 9508c2ecf20Sopenharmony_ci * Description: Stops the state machine delayed workqueue, sets the 9518c2ecf20Sopenharmony_ci * state to UP (unless it wasn't up yet). This function must be 9528c2ecf20Sopenharmony_ci * called BEFORE phy_detach. 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_civoid phy_stop_machine(struct phy_device *phydev) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&phydev->state_queue); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 9598c2ecf20Sopenharmony_ci if (phy_is_started(phydev)) 9608c2ecf20Sopenharmony_ci phydev->state = PHY_UP; 9618c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/** 9658c2ecf20Sopenharmony_ci * phy_error - enter HALTED state for this PHY device 9668c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 9678c2ecf20Sopenharmony_ci * 9688c2ecf20Sopenharmony_ci * Moves the PHY to the HALTED state in response to a read 9698c2ecf20Sopenharmony_ci * or write error, and tells the controller the link is down. 9708c2ecf20Sopenharmony_ci * Must not be called from interrupt context, or while the 9718c2ecf20Sopenharmony_ci * phydev->lock is held. 9728c2ecf20Sopenharmony_ci */ 9738c2ecf20Sopenharmony_cistatic void phy_error(struct phy_device *phydev) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci WARN_ON(1); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 9788c2ecf20Sopenharmony_ci phydev->state = PHY_HALTED; 9798c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci phy_trigger_machine(phydev); 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci/** 9858c2ecf20Sopenharmony_ci * phy_disable_interrupts - Disable the PHY interrupts from the PHY side 9868c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ciint phy_disable_interrupts(struct phy_device *phydev) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci int err; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Disable PHY interrupts */ 9938c2ecf20Sopenharmony_ci err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); 9948c2ecf20Sopenharmony_ci if (err) 9958c2ecf20Sopenharmony_ci return err; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* Clear the interrupt */ 9988c2ecf20Sopenharmony_ci return phy_clear_interrupt(phydev); 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/** 10028c2ecf20Sopenharmony_ci * phy_did_interrupt - Checks if the PHY generated an interrupt 10038c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_cistatic int phy_did_interrupt(struct phy_device *phydev) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci int ret; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 10108c2ecf20Sopenharmony_ci ret = phydev->drv->did_interrupt(phydev); 10118c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci return ret; 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci/** 10178c2ecf20Sopenharmony_ci * phy_handle_interrupt - Handle PHY interrupt 10188c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_cistatic irqreturn_t phy_handle_interrupt(struct phy_device *phydev) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci irqreturn_t ret; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 10258c2ecf20Sopenharmony_ci ret = phydev->drv->handle_interrupt(phydev); 10268c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci return ret; 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci/** 10328c2ecf20Sopenharmony_ci * phy_interrupt - PHY interrupt handler 10338c2ecf20Sopenharmony_ci * @irq: interrupt line 10348c2ecf20Sopenharmony_ci * @phy_dat: phy_device pointer 10358c2ecf20Sopenharmony_ci * 10368c2ecf20Sopenharmony_ci * Description: Handle PHY interrupt 10378c2ecf20Sopenharmony_ci */ 10388c2ecf20Sopenharmony_cistatic irqreturn_t phy_interrupt(int irq, void *phy_dat) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci struct phy_device *phydev = phy_dat; 10418c2ecf20Sopenharmony_ci struct phy_driver *drv = phydev->drv; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (drv->handle_interrupt) 10448c2ecf20Sopenharmony_ci return phy_handle_interrupt(phydev); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (drv->did_interrupt && !phy_did_interrupt(phydev)) 10478c2ecf20Sopenharmony_ci return IRQ_NONE; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* reschedule state queue work to run as soon as possible */ 10508c2ecf20Sopenharmony_ci phy_trigger_machine(phydev); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* did_interrupt() may have cleared the interrupt already */ 10538c2ecf20Sopenharmony_ci if (!drv->did_interrupt && phy_clear_interrupt(phydev)) { 10548c2ecf20Sopenharmony_ci phy_error(phydev); 10558c2ecf20Sopenharmony_ci return IRQ_NONE; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci/** 10628c2ecf20Sopenharmony_ci * phy_enable_interrupts - Enable the interrupts from the PHY side 10638c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_cistatic int phy_enable_interrupts(struct phy_device *phydev) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci int err = phy_clear_interrupt(phydev); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (err < 0) 10708c2ecf20Sopenharmony_ci return err; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci return phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci/** 10768c2ecf20Sopenharmony_ci * phy_request_interrupt - request and enable interrupt for a PHY device 10778c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 10788c2ecf20Sopenharmony_ci * 10798c2ecf20Sopenharmony_ci * Description: Request and enable the interrupt for the given PHY. 10808c2ecf20Sopenharmony_ci * If this fails, then we set irq to PHY_POLL. 10818c2ecf20Sopenharmony_ci * This should only be called with a valid IRQ number. 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_civoid phy_request_interrupt(struct phy_device *phydev) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci int err; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci err = request_threaded_irq(phydev->irq, NULL, phy_interrupt, 10888c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, 10898c2ecf20Sopenharmony_ci phydev_name(phydev), phydev); 10908c2ecf20Sopenharmony_ci if (err) { 10918c2ecf20Sopenharmony_ci phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n", 10928c2ecf20Sopenharmony_ci err, phydev->irq); 10938c2ecf20Sopenharmony_ci phydev->irq = PHY_POLL; 10948c2ecf20Sopenharmony_ci } else { 10958c2ecf20Sopenharmony_ci if (phy_enable_interrupts(phydev)) { 10968c2ecf20Sopenharmony_ci phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n"); 10978c2ecf20Sopenharmony_ci phy_free_interrupt(phydev); 10988c2ecf20Sopenharmony_ci phydev->irq = PHY_POLL; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_request_interrupt); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci/** 11058c2ecf20Sopenharmony_ci * phy_free_interrupt - disable and free interrupt for a PHY device 11068c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 11078c2ecf20Sopenharmony_ci * 11088c2ecf20Sopenharmony_ci * Description: Disable and free the interrupt for the given PHY. 11098c2ecf20Sopenharmony_ci * This should only be called with a valid IRQ number. 11108c2ecf20Sopenharmony_ci */ 11118c2ecf20Sopenharmony_civoid phy_free_interrupt(struct phy_device *phydev) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci phy_disable_interrupts(phydev); 11148c2ecf20Sopenharmony_ci free_irq(phydev->irq, phydev); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_free_interrupt); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci/** 11198c2ecf20Sopenharmony_ci * phy_stop - Bring down the PHY link, and stop checking the status 11208c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 11218c2ecf20Sopenharmony_ci */ 11228c2ecf20Sopenharmony_civoid phy_stop(struct phy_device *phydev) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 11258c2ecf20Sopenharmony_ci enum phy_state old_state; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (!phy_is_started(phydev) && phydev->state != PHY_DOWN) { 11288c2ecf20Sopenharmony_ci WARN(1, "called from state %s\n", 11298c2ecf20Sopenharmony_ci phy_state_to_str(phydev->state)); 11308c2ecf20Sopenharmony_ci return; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 11348c2ecf20Sopenharmony_ci old_state = phydev->state; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (phydev->state == PHY_CABLETEST) { 11378c2ecf20Sopenharmony_ci phy_abort_cable_test(phydev); 11388c2ecf20Sopenharmony_ci netif_testing_off(dev); 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (phydev->sfp_bus) 11428c2ecf20Sopenharmony_ci sfp_upstream_stop(phydev->sfp_bus); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci phydev->state = PHY_HALTED; 11458c2ecf20Sopenharmony_ci phy_process_state_change(phydev, old_state); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci phy_state_machine(&phydev->state_queue.work); 11508c2ecf20Sopenharmony_ci phy_stop_machine(phydev); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* Cannot call flush_scheduled_work() here as desired because 11538c2ecf20Sopenharmony_ci * of rtnl_lock(), but PHY_HALTED shall guarantee irq handler 11548c2ecf20Sopenharmony_ci * will not reenable interrupts. 11558c2ecf20Sopenharmony_ci */ 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_stop); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci/** 11608c2ecf20Sopenharmony_ci * phy_start - start or restart a PHY device 11618c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 11628c2ecf20Sopenharmony_ci * 11638c2ecf20Sopenharmony_ci * Description: Indicates the attached device's readiness to 11648c2ecf20Sopenharmony_ci * handle PHY-related work. Used during startup to start the 11658c2ecf20Sopenharmony_ci * PHY, and after a call to phy_stop() to resume operation. 11668c2ecf20Sopenharmony_ci * Also used to indicate the MDIO bus has cleared an error 11678c2ecf20Sopenharmony_ci * condition. 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_civoid phy_start(struct phy_device *phydev) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (phydev->state != PHY_READY && phydev->state != PHY_HALTED) { 11748c2ecf20Sopenharmony_ci WARN(1, "called from state %s\n", 11758c2ecf20Sopenharmony_ci phy_state_to_str(phydev->state)); 11768c2ecf20Sopenharmony_ci goto out; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (phydev->sfp_bus) 11808c2ecf20Sopenharmony_ci sfp_upstream_start(phydev->sfp_bus); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* if phy was suspended, bring the physical link up again */ 11838c2ecf20Sopenharmony_ci __phy_resume(phydev); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci phydev->state = PHY_UP; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci phy_start_machine(phydev); 11888c2ecf20Sopenharmony_ciout: 11898c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_start); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci/** 11948c2ecf20Sopenharmony_ci * phy_state_machine - Handle the state machine 11958c2ecf20Sopenharmony_ci * @work: work_struct that describes the work to be done 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_civoid phy_state_machine(struct work_struct *work) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 12008c2ecf20Sopenharmony_ci struct phy_device *phydev = 12018c2ecf20Sopenharmony_ci container_of(dwork, struct phy_device, state_queue); 12028c2ecf20Sopenharmony_ci struct net_device *dev = phydev->attached_dev; 12038c2ecf20Sopenharmony_ci bool needs_aneg = false, do_suspend = false; 12048c2ecf20Sopenharmony_ci enum phy_state old_state; 12058c2ecf20Sopenharmony_ci bool finished = false; 12068c2ecf20Sopenharmony_ci int err = 0; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci old_state = phydev->state; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci switch (phydev->state) { 12138c2ecf20Sopenharmony_ci case PHY_DOWN: 12148c2ecf20Sopenharmony_ci case PHY_READY: 12158c2ecf20Sopenharmony_ci break; 12168c2ecf20Sopenharmony_ci case PHY_UP: 12178c2ecf20Sopenharmony_ci needs_aneg = true; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci break; 12208c2ecf20Sopenharmony_ci case PHY_NOLINK: 12218c2ecf20Sopenharmony_ci case PHY_RUNNING: 12228c2ecf20Sopenharmony_ci err = phy_check_link_status(phydev); 12238c2ecf20Sopenharmony_ci break; 12248c2ecf20Sopenharmony_ci case PHY_CABLETEST: 12258c2ecf20Sopenharmony_ci err = phydev->drv->cable_test_get_status(phydev, &finished); 12268c2ecf20Sopenharmony_ci if (err) { 12278c2ecf20Sopenharmony_ci phy_abort_cable_test(phydev); 12288c2ecf20Sopenharmony_ci netif_testing_off(dev); 12298c2ecf20Sopenharmony_ci needs_aneg = true; 12308c2ecf20Sopenharmony_ci phydev->state = PHY_UP; 12318c2ecf20Sopenharmony_ci break; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (finished) { 12358c2ecf20Sopenharmony_ci ethnl_cable_test_finished(phydev); 12368c2ecf20Sopenharmony_ci netif_testing_off(dev); 12378c2ecf20Sopenharmony_ci needs_aneg = true; 12388c2ecf20Sopenharmony_ci phydev->state = PHY_UP; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci break; 12418c2ecf20Sopenharmony_ci case PHY_HALTED: 12428c2ecf20Sopenharmony_ci if (phydev->link) { 12438c2ecf20Sopenharmony_ci phydev->link = 0; 12448c2ecf20Sopenharmony_ci phy_link_down(phydev); 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci do_suspend = true; 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (needs_aneg) 12538c2ecf20Sopenharmony_ci err = phy_start_aneg(phydev); 12548c2ecf20Sopenharmony_ci else if (do_suspend) 12558c2ecf20Sopenharmony_ci phy_suspend(phydev); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci if (err < 0) 12588c2ecf20Sopenharmony_ci phy_error(phydev); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci phy_process_state_change(phydev, old_state); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* Only re-schedule a PHY state machine change if we are polling the 12638c2ecf20Sopenharmony_ci * PHY, if PHY_IGNORE_INTERRUPT is set, then we will be moving 12648c2ecf20Sopenharmony_ci * between states from phy_mac_interrupt(). 12658c2ecf20Sopenharmony_ci * 12668c2ecf20Sopenharmony_ci * In state PHY_HALTED the PHY gets suspended, so rescheduling the 12678c2ecf20Sopenharmony_ci * state machine would be pointless and possibly error prone when 12688c2ecf20Sopenharmony_ci * called from phy_disconnect() synchronously. 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 12718c2ecf20Sopenharmony_ci if (phy_polling_mode(phydev) && phy_is_started(phydev)) 12728c2ecf20Sopenharmony_ci phy_queue_state_machine(phydev, PHY_STATE_TIME); 12738c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci/** 12778c2ecf20Sopenharmony_ci * phy_mac_interrupt - MAC says the link has changed 12788c2ecf20Sopenharmony_ci * @phydev: phy_device struct with changed link 12798c2ecf20Sopenharmony_ci * 12808c2ecf20Sopenharmony_ci * The MAC layer is able to indicate there has been a change in the PHY link 12818c2ecf20Sopenharmony_ci * status. Trigger the state machine and work a work queue. 12828c2ecf20Sopenharmony_ci */ 12838c2ecf20Sopenharmony_civoid phy_mac_interrupt(struct phy_device *phydev) 12848c2ecf20Sopenharmony_ci{ 12858c2ecf20Sopenharmony_ci /* Trigger a state machine change */ 12868c2ecf20Sopenharmony_ci phy_trigger_machine(phydev); 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_mac_interrupt); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci linkmode_zero(advertising); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if (eee_adv & MDIO_EEE_100TX) 12958c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 12968c2ecf20Sopenharmony_ci advertising); 12978c2ecf20Sopenharmony_ci if (eee_adv & MDIO_EEE_1000T) 12988c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 12998c2ecf20Sopenharmony_ci advertising); 13008c2ecf20Sopenharmony_ci if (eee_adv & MDIO_EEE_10GT) 13018c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, 13028c2ecf20Sopenharmony_ci advertising); 13038c2ecf20Sopenharmony_ci if (eee_adv & MDIO_EEE_1000KX) 13048c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, 13058c2ecf20Sopenharmony_ci advertising); 13068c2ecf20Sopenharmony_ci if (eee_adv & MDIO_EEE_10GKX4) 13078c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, 13088c2ecf20Sopenharmony_ci advertising); 13098c2ecf20Sopenharmony_ci if (eee_adv & MDIO_EEE_10GKR) 13108c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 13118c2ecf20Sopenharmony_ci advertising); 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci/** 13158c2ecf20Sopenharmony_ci * phy_init_eee - init and check the EEE feature 13168c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 13178c2ecf20Sopenharmony_ci * @clk_stop_enable: PHY may stop the clock during LPI 13188c2ecf20Sopenharmony_ci * 13198c2ecf20Sopenharmony_ci * Description: it checks if the Energy-Efficient Ethernet (EEE) 13208c2ecf20Sopenharmony_ci * is supported by looking at the MMD registers 3.20 and 7.60/61 13218c2ecf20Sopenharmony_ci * and it programs the MMD register 3.0 setting the "Clock stop enable" 13228c2ecf20Sopenharmony_ci * bit if required. 13238c2ecf20Sopenharmony_ci */ 13248c2ecf20Sopenharmony_ciint phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) 13258c2ecf20Sopenharmony_ci{ 13268c2ecf20Sopenharmony_ci if (!phydev->drv) 13278c2ecf20Sopenharmony_ci return -EIO; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* According to 802.3az,the EEE is supported only in full duplex-mode. 13308c2ecf20Sopenharmony_ci */ 13318c2ecf20Sopenharmony_ci if (phydev->duplex == DUPLEX_FULL) { 13328c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(common); 13338c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(lp); 13348c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(adv); 13358c2ecf20Sopenharmony_ci int eee_lp, eee_cap, eee_adv; 13368c2ecf20Sopenharmony_ci int status; 13378c2ecf20Sopenharmony_ci u32 cap; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci /* Read phy status to properly get the right settings */ 13408c2ecf20Sopenharmony_ci status = phy_read_status(phydev); 13418c2ecf20Sopenharmony_ci if (status) 13428c2ecf20Sopenharmony_ci return status; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci /* First check if the EEE ability is supported */ 13458c2ecf20Sopenharmony_ci eee_cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); 13468c2ecf20Sopenharmony_ci if (eee_cap <= 0) 13478c2ecf20Sopenharmony_ci goto eee_exit_err; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap); 13508c2ecf20Sopenharmony_ci if (!cap) 13518c2ecf20Sopenharmony_ci goto eee_exit_err; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* Check which link settings negotiated and verify it in 13548c2ecf20Sopenharmony_ci * the EEE advertising registers. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_ci eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); 13578c2ecf20Sopenharmony_ci if (eee_lp <= 0) 13588c2ecf20Sopenharmony_ci goto eee_exit_err; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci eee_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); 13618c2ecf20Sopenharmony_ci if (eee_adv <= 0) 13628c2ecf20Sopenharmony_ci goto eee_exit_err; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci mmd_eee_adv_to_linkmode(adv, eee_adv); 13658c2ecf20Sopenharmony_ci mmd_eee_adv_to_linkmode(lp, eee_lp); 13668c2ecf20Sopenharmony_ci linkmode_and(common, adv, lp); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (!phy_check_valid(phydev->speed, phydev->duplex, common)) 13698c2ecf20Sopenharmony_ci goto eee_exit_err; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (clk_stop_enable) 13728c2ecf20Sopenharmony_ci /* Configure the PHY to stop receiving xMII 13738c2ecf20Sopenharmony_ci * clock while it is signaling LPI. 13748c2ecf20Sopenharmony_ci */ 13758c2ecf20Sopenharmony_ci phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, 13768c2ecf20Sopenharmony_ci MDIO_PCS_CTRL1_CLKSTOP_EN); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci return 0; /* EEE supported */ 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_cieee_exit_err: 13818c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_init_eee); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci/** 13868c2ecf20Sopenharmony_ci * phy_get_eee_err - report the EEE wake error count 13878c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 13888c2ecf20Sopenharmony_ci * 13898c2ecf20Sopenharmony_ci * Description: it is to report the number of time where the PHY 13908c2ecf20Sopenharmony_ci * failed to complete its normal wake sequence. 13918c2ecf20Sopenharmony_ci */ 13928c2ecf20Sopenharmony_ciint phy_get_eee_err(struct phy_device *phydev) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci if (!phydev->drv) 13958c2ecf20Sopenharmony_ci return -EIO; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci return phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR); 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_get_eee_err); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci/** 14028c2ecf20Sopenharmony_ci * phy_ethtool_get_eee - get EEE supported and status 14038c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 14048c2ecf20Sopenharmony_ci * @data: ethtool_eee data 14058c2ecf20Sopenharmony_ci * 14068c2ecf20Sopenharmony_ci * Description: it reportes the Supported/Advertisement/LP Advertisement 14078c2ecf20Sopenharmony_ci * capabilities. 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_ciint phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci int val; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci if (!phydev->drv) 14148c2ecf20Sopenharmony_ci return -EIO; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci /* Get Supported EEE */ 14178c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); 14188c2ecf20Sopenharmony_ci if (val < 0) 14198c2ecf20Sopenharmony_ci return val; 14208c2ecf20Sopenharmony_ci data->supported = mmd_eee_cap_to_ethtool_sup_t(val); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci /* Get advertisement EEE */ 14238c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); 14248c2ecf20Sopenharmony_ci if (val < 0) 14258c2ecf20Sopenharmony_ci return val; 14268c2ecf20Sopenharmony_ci data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); 14278c2ecf20Sopenharmony_ci data->eee_enabled = !!data->advertised; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci /* Get LP advertisement EEE */ 14308c2ecf20Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); 14318c2ecf20Sopenharmony_ci if (val < 0) 14328c2ecf20Sopenharmony_ci return val; 14338c2ecf20Sopenharmony_ci data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci data->eee_active = !!(data->advertised & data->lp_advertised); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci return 0; 14388c2ecf20Sopenharmony_ci} 14398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_eee); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci/** 14428c2ecf20Sopenharmony_ci * phy_ethtool_set_eee - set EEE supported and status 14438c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 14448c2ecf20Sopenharmony_ci * @data: ethtool_eee data 14458c2ecf20Sopenharmony_ci * 14468c2ecf20Sopenharmony_ci * Description: it is to program the Advertisement EEE register. 14478c2ecf20Sopenharmony_ci */ 14488c2ecf20Sopenharmony_ciint phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) 14498c2ecf20Sopenharmony_ci{ 14508c2ecf20Sopenharmony_ci int cap, old_adv, adv = 0, ret; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (!phydev->drv) 14538c2ecf20Sopenharmony_ci return -EIO; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci /* Get Supported EEE */ 14568c2ecf20Sopenharmony_ci cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); 14578c2ecf20Sopenharmony_ci if (cap < 0) 14588c2ecf20Sopenharmony_ci return cap; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); 14618c2ecf20Sopenharmony_ci if (old_adv < 0) 14628c2ecf20Sopenharmony_ci return old_adv; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (data->eee_enabled) { 14658c2ecf20Sopenharmony_ci adv = !data->advertised ? cap : 14668c2ecf20Sopenharmony_ci ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; 14678c2ecf20Sopenharmony_ci /* Mask prohibited EEE modes */ 14688c2ecf20Sopenharmony_ci adv &= ~phydev->eee_broken_modes; 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (old_adv != adv) { 14728c2ecf20Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); 14738c2ecf20Sopenharmony_ci if (ret < 0) 14748c2ecf20Sopenharmony_ci return ret; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci /* Restart autonegotiation so the new modes get sent to the 14778c2ecf20Sopenharmony_ci * link partner. 14788c2ecf20Sopenharmony_ci */ 14798c2ecf20Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE) { 14808c2ecf20Sopenharmony_ci ret = phy_restart_aneg(phydev); 14818c2ecf20Sopenharmony_ci if (ret < 0) 14828c2ecf20Sopenharmony_ci return ret; 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci return 0; 14878c2ecf20Sopenharmony_ci} 14888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_set_eee); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci/** 14918c2ecf20Sopenharmony_ci * phy_ethtool_set_wol - Configure Wake On LAN 14928c2ecf20Sopenharmony_ci * 14938c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 14948c2ecf20Sopenharmony_ci * @wol: Configuration requested 14958c2ecf20Sopenharmony_ci */ 14968c2ecf20Sopenharmony_ciint phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci if (phydev->drv && phydev->drv->set_wol) 14998c2ecf20Sopenharmony_ci return phydev->drv->set_wol(phydev, wol); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_set_wol); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci/** 15068c2ecf20Sopenharmony_ci * phy_ethtool_get_wol - Get the current Wake On LAN configuration 15078c2ecf20Sopenharmony_ci * 15088c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 15098c2ecf20Sopenharmony_ci * @wol: Store the current configuration here 15108c2ecf20Sopenharmony_ci */ 15118c2ecf20Sopenharmony_civoid phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci if (phydev->drv && phydev->drv->get_wol) 15148c2ecf20Sopenharmony_ci phydev->drv->get_wol(phydev, wol); 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_wol); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ciint phy_ethtool_get_link_ksettings(struct net_device *ndev, 15198c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci if (!phydev) 15248c2ecf20Sopenharmony_ci return -ENODEV; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci phy_ethtool_ksettings_get(phydev, cmd); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci return 0; 15298c2ecf20Sopenharmony_ci} 15308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_get_link_ksettings); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ciint phy_ethtool_set_link_ksettings(struct net_device *ndev, 15338c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci if (!phydev) 15388c2ecf20Sopenharmony_ci return -ENODEV; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci return phy_ethtool_ksettings_set(phydev, cmd); 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_set_link_ksettings); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci/** 15458c2ecf20Sopenharmony_ci * phy_ethtool_nway_reset - Restart auto negotiation 15468c2ecf20Sopenharmony_ci * @ndev: Network device to restart autoneg for 15478c2ecf20Sopenharmony_ci */ 15488c2ecf20Sopenharmony_ciint phy_ethtool_nway_reset(struct net_device *ndev) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (!phydev) 15538c2ecf20Sopenharmony_ci return -ENODEV; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (!phydev->drv) 15568c2ecf20Sopenharmony_ci return -EIO; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci return phy_restart_aneg(phydev); 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_ethtool_nway_reset); 1561