/* Framework for configuring and reading PHY devices * Based on code in sungem_phy.c and gianfar_phy.c * * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. * Copyright (c) 2006, 2007 Maciej W. Rozycki * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/unistd.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/phy.h>#include <linux/phy_led_triggers.h>#include <linux/timer.h>#include <linux/workqueue.h>#include <linux/mdio.h>#include <linux/io.h>#include <linux/uaccess.h>#include <linux/atomic.h> #include <asm/irq.h> static const char *phy_speed_to_str(int speed){ switch (speed) { case SPEED_10: return "10Mbps"; case SPEED_100: return "100Mbps"; case SPEED_1000: return "1Gbps"; case SPEED_2500: return "2.5Gbps"; case SPEED_5000: return "5Gbps"; case SPEED_10000: return "10Gbps"; case SPEED_14000: return "14Gbps"; case SPEED_20000: return "20Gbps"; case SPEED_25000: return "25Gbps"; case SPEED_40000: return "40Gbps"; case SPEED_50000: return "50Gbps"; case SPEED_56000: return "56Gbps"; case SPEED_100000: return "100Gbps"; case SPEED_UNKNOWN: return "Unknown"; default: return "Unsupported (update phy.c)"; }} #define PHY_STATE_STR(_state) \ case PHY_##_state: \ return __stringify(_state); \ static const char *phy_state_to_str(enum phy_state st){ switch (st) { PHY_STATE_STR(DOWN) PHY_STATE_STR(STARTING) PHY_STATE_STR(READY) PHY_STATE_STR(PENDING) PHY_STATE_STR(UP) PHY_STATE_STR(AN) PHY_STATE_STR(RUNNING) PHY_STATE_STR(NOLINK) PHY_STATE_STR(FORCING) PHY_STATE_STR(CHANGELINK) PHY_STATE_STR(HALTED) PHY_STATE_STR(RESUMING) } return NULL;} /** * phy_print_status - Convenience function to print out the current phy status * @phydev: the phy_device struct */void phy_print_status(struct phy_device *phydev){ if (phydev->link) { netdev_info(phydev->attached_dev, "Link is Up - %s/%s - flow control %s\n", phy_speed_to_str(phydev->speed), DUPLEX_FULL == phydev->duplex ? "Full" : "Half", phydev->pause ? "rx/tx" : "off"); } else { netdev_info(phydev->attached_dev, "Link is Down\n"); }}EXPORT_SYMBOL(phy_print_status); /** * phy_clear_interrupt - Ack the phy device's interrupt * @phydev: the phy_device struct * * If the @phydev driver has an ack_interrupt function, call it to * ack and clear the phy device's interrupt. * * Returns 0 on success or < 0 on error. */static int phy_clear_interrupt(struct phy_device *phydev){ if (phydev->drv->ack_interrupt) return phydev->drv->ack_interrupt(phydev); return 0;} /** * phy_config_interrupt - configure the PHY device for the requested interrupts * @phydev: the phy_device struct * @interrupts: interrupt flags to configure for this @phydev * * Returns 0 on success or < 0 on error. */static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts){ phydev->interrupts = interrupts; if (phydev->drv->config_intr) return phydev->drv->config_intr(phydev); return 0;} /** * phy_restart_aneg - restart auto-negotiation * @phydev: target phy_device struct * * Restart the autonegotiation on @phydev. Returns >= 0 on success or * negative errno on error. */int phy_restart_aneg(struct phy_device *phydev){ int ret; if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) ret = genphy_c45_restart_aneg(phydev); else ret = genphy_restart_aneg(phydev); return ret;}EXPORT_SYMBOL_GPL(phy_restart_aneg); /** * phy_aneg_done - return auto-negotiation status * @phydev: target phy_device struct * * Description: Return the auto-negotiation status from this @phydev * Returns > 0 on success or < 0 on error. 0 means that auto-negotiation * is still pending. */int phy_aneg_done(struct phy_device *phydev){ if (phydev->drv && phydev->drv->aneg_done) return phydev->drv->aneg_done(phydev); /* Avoid genphy_aneg_done() if the Clause 45 PHY does not * implement Clause 22 registers */ if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) return -EINVAL; return genphy_aneg_done(phydev);}EXPORT_SYMBOL(phy_aneg_done); /* A structure for mapping a particular speed and duplex * combination to a particular SUPPORTED and ADVERTISED value */struct phy_setting { int speed; int duplex; u32 setting;}; /* A mapping of all SUPPORTED settings to speed/duplex. This table * must be grouped by speed and sorted in descending match priority * - iow, descending speed. */static const struct phy_setting settings[] = { { .speed = SPEED_10000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10000baseKR_Full, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10000baseKX4_Full, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10000baseT_Full, }, { .speed = SPEED_2500, .duplex = DUPLEX_FULL, .setting = SUPPORTED_2500baseX_Full, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_1000baseKX_Full, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_1000baseT_Full, }, { .speed = SPEED_1000, .duplex = DUPLEX_HALF, .setting = SUPPORTED_1000baseT_Half, }, { .speed = SPEED_100, .duplex = DUPLEX_FULL, .setting = SUPPORTED_100baseT_Full, }, { .speed = SPEED_100, .duplex = DUPLEX_HALF, .setting = SUPPORTED_100baseT_Half, }, { .speed = SPEED_10, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10baseT_Full, }, { .speed = SPEED_10, .duplex = DUPLEX_HALF, .setting = SUPPORTED_10baseT_Half, },}; /** * phy_lookup_setting - lookup a PHY setting * @speed: speed to match * @duplex: duplex to match * @features: allowed link modes * @exact: an exact match is required * * Search the settings array for a setting that matches the speed and * duplex, and which is supported. * * If @exact is unset, either an exact match or %NULL for no match will * be returned. * * If @exact is set, an exact match, the fastest supported setting at * or below the specified speed, the slowest supported setting, or if * they all fail, %NULL will be returned. */static const struct phy_setting *phy_lookup_setting(int speed, int duplex, u32 features, bool exact){ const struct phy_setting *p, *match = NULL, *last = NULL; int i; for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { if (p->setting & features) { last = p; if (p->speed == speed && p->duplex == duplex) { /* Exact match for speed and duplex */ match = p; break; } else if (!exact) { if (!match && p->speed <= speed) /* Candidate */ match = p; if (p->speed < speed) break; } } } if (!match && !exact) match = last; return match;} /** * phy_find_valid - find a PHY setting that matches the requested parameters * @speed: desired speed * @duplex: desired duplex * @supported: mask of supported link modes * * Locate a supported phy setting that is, in priority order: * - an exact match for the specified speed and duplex mode * - a match for the specified speed, or slower speed * - the slowest supported speed * Returns the matched phy_setting entry, or %NULL if no supported phy * settings were found. */static const struct phy_setting *phy_find_valid(int speed, int duplex, u32 supported){ return phy_lookup_setting(speed, duplex, supported, false);} /** * phy_supported_speeds - return all speeds currently supported by a phy device * @phy: The phy device to return supported speeds of. * @speeds: buffer to store supported speeds in. * @size: size of speeds buffer. * * Description: Returns the number of supported speeds, and fills the speeds * buffer with the supported speeds. If speeds buffer is too small to contain * all currently supported speeds, will return as many speeds as can fit. */unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int *speeds, unsigned int size){ unsigned int count = 0; unsigned int idx = 0; for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) /* Assumes settings are grouped by speed */ if ((settings[idx].setting & phy->supported) && (count == 0 || speeds[count - 1] != settings[idx].speed)) speeds[count++] = settings[idx].speed; return count;} /** * phy_check_valid - check if there is a valid PHY setting which matches * speed, duplex, and feature mask * @speed: speed to match * @duplex: duplex to match * @features: A mask of the valid settings * * Description: Returns true if there is a valid setting, false otherwise. */static inline bool phy_check_valid(int speed, int duplex, u32 features){ return !!phy_lookup_setting(speed, duplex, features, true);} /** * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex * @phydev: the target phy_device struct * * Description: Make sure the PHY is set to supported speeds and * duplexes. Drop down by one in this order: 1000/FULL, * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. */static void phy_sanitize_settings(struct phy_device *phydev){ const struct phy_setting *setting; u32 features = phydev->supported; /* Sanitize settings based on PHY capabilities */ if ((features & SUPPORTED_Autoneg) == 0) phydev->autoneg = AUTONEG_DISABLE; setting = phy_find_valid(phydev->speed, phydev->duplex, features); if (setting) { phydev->speed = setting->speed; phydev->duplex = setting->duplex; } else { /* We failed to find anything (no supported speeds?) */ phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; }} /** * phy_ethtool_sset - generic ethtool sset function, handles all the details * @phydev: target phy_device struct * @cmd: ethtool_cmd * * A few notes about parameter checking: * * - We don't set port or transceiver, so we don't care what they * were set to. * - phy_start_aneg() will make sure forced settings are sane, and * choose the next best ones from the ones selected, so we don't * care if ethtool tries to give us bad values. */int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd){ u32 speed = ethtool_cmd_speed(cmd); if (cmd->phy_address != phydev->mdio.addr) return -EINVAL; /* We make sure that we don't pass unsupported values in to the PHY */ cmd->advertising &= phydev->supported; /* Verify the settings we care about. */ if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE) return -EINVAL; if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0) return -EINVAL; if (cmd->autoneg == AUTONEG_DISABLE && ((speed != SPEED_1000 && speed != SPEED_100 && speed != SPEED_10) || (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL))) return -EINVAL; phydev->autoneg = cmd->autoneg; phydev->speed = speed; phydev->advertising = cmd->advertising; if (AUTONEG_ENABLE == cmd->autoneg) phydev->advertising |= ADVERTISED_Autoneg; else phydev->advertising &= ~ADVERTISED_Autoneg; phydev->duplex = cmd->duplex; phydev->mdix_ctrl = cmd->eth_tp_mdix_ctrl; /* Restart the PHY */ phy_start_aneg(phydev); return 0;}EXPORT_SYMBOL(phy_ethtool_sset); int phy_ethtool_ksettings_set(struct phy_device *phydev, const struct ethtool_link_ksettings *cmd){ u8 autoneg = cmd->base.autoneg; u8 duplex = cmd->base.duplex; u32 speed = cmd->base.speed; u32 advertising; if (cmd->base.phy_address != phydev->mdio.addr) return -EINVAL; ethtool_convert_link_mode_to_legacy_u32(&advertising, cmd->link_modes.advertising); /* We make sure that we don't pass unsupported values in to the PHY */ advertising &= phydev->supported; /* Verify the settings we care about. */ if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE) return -EINVAL; if (autoneg == AUTONEG_ENABLE && advertising == 0) return -EINVAL; if (autoneg == AUTONEG_DISABLE && ((speed != SPEED_1000 && speed != SPEED_100 && speed != SPEED_10) || (duplex != DUPLEX_HALF && duplex != DUPLEX_FULL))) return -EINVAL; phydev->autoneg = autoneg; phydev->speed = speed; phydev->advertising = advertising; if (autoneg == AUTONEG_ENABLE) phydev->advertising |= ADVERTISED_Autoneg;Lines 1–500 of 1483. Load more
/* Framework for configuring and reading PHY devices * Based on code in sungem_phy.c and gianfar_phy.c * * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. * Copyright (c) 2006, 2007 Maciej W. Rozycki * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/unistd.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/phy.h> #include <linux/phy_led_triggers.h> #include <linux/timer.h> #include <linux/workqueue.h> #include <linux/mdio.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/atomic.h> #include <asm/irq.h> static const char *phy_speed_to_str(int speed) { switch (speed) { case SPEED_10: return "10Mbps"; case SPEED_100: return "100Mbps"; case SPEED_1000: return "1Gbps"; case SPEED_2500: return "2.5Gbps"; case SPEED_5000: return "5Gbps"; case SPEED_10000: return "10Gbps"; case SPEED_14000: return "14Gbps"; case SPEED_20000: return "20Gbps"; case SPEED_25000: return "25Gbps"; case SPEED_40000: return "40Gbps"; case SPEED_50000: return "50Gbps"; case SPEED_56000: return "56Gbps"; case SPEED_100000: return "100Gbps"; case SPEED_UNKNOWN: return "Unknown"; default: return "Unsupported (update phy.c)"; } } #define PHY_STATE_STR(_state) \ case PHY_##_state: \ return __stringify(_state); \ static const char *phy_state_to_str(enum phy_state st) { switch (st) { PHY_STATE_STR(DOWN) PHY_STATE_STR(STARTING) PHY_STATE_STR(READY) PHY_STATE_STR(PENDING) PHY_STATE_STR(UP) PHY_STATE_STR(AN) PHY_STATE_STR(RUNNING) PHY_STATE_STR(NOLINK) PHY_STATE_STR(FORCING) PHY_STATE_STR(CHANGELINK) PHY_STATE_STR(HALTED) PHY_STATE_STR(RESUMING) } return NULL; } /** * phy_print_status - Convenience function to print out the current phy status * @phydev: the phy_device struct */ void phy_print_status(struct phy_device *phydev) { if (phydev->link) { netdev_info(phydev->attached_dev, "Link is Up - %s/%s - flow control %s\n", phy_speed_to_str(phydev->speed), DUPLEX_FULL == phydev->duplex ? "Full" : "Half", phydev->pause ? "rx/tx" : "off"); } else { netdev_info(phydev->attached_dev, "Link is Down\n"); } } EXPORT_SYMBOL(phy_print_status); /** * phy_clear_interrupt - Ack the phy device's interrupt * @phydev: the phy_device struct * * If the @phydev driver has an ack_interrupt function, call it to * ack and clear the phy device's interrupt. * * Returns 0 on success or < 0 on error. */ static int phy_clear_interrupt(struct phy_device *phydev) { if (phydev->drv->ack_interrupt) return phydev->drv->ack_interrupt(phydev); return 0; } /** * phy_config_interrupt - configure the PHY device for the requested interrupts * @phydev: the phy_device struct * @interrupts: interrupt flags to configure for this @phydev * * Returns 0 on success or < 0 on error. */ static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) { phydev->interrupts = interrupts; if (phydev->drv->config_intr) return phydev->drv->config_intr(phydev); return 0; } /** * phy_restart_aneg - restart auto-negotiation * @phydev: target phy_device struct * * Restart the autonegotiation on @phydev. Returns >= 0 on success or * negative errno on error. */ int phy_restart_aneg(struct phy_device *phydev) { int ret; if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) ret = genphy_c45_restart_aneg(phydev); else ret = genphy_restart_aneg(phydev); return ret; } EXPORT_SYMBOL_GPL(phy_restart_aneg); /** * phy_aneg_done - return auto-negotiation status * @phydev: target phy_device struct * * Description: Return the auto-negotiation status from this @phydev * Returns > 0 on success or < 0 on error. 0 means that auto-negotiation * is still pending. */ int phy_aneg_done(struct phy_device *phydev) { if (phydev->drv && phydev->drv->aneg_done) return phydev->drv->aneg_done(phydev); /* Avoid genphy_aneg_done() if the Clause 45 PHY does not * implement Clause 22 registers */ if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) return -EINVAL; return genphy_aneg_done(phydev); } EXPORT_SYMBOL(phy_aneg_done); /* A structure for mapping a particular speed and duplex * combination to a particular SUPPORTED and ADVERTISED value */ struct phy_setting { int speed; int duplex; u32 setting; }; /* A mapping of all SUPPORTED settings to speed/duplex. This table * must be grouped by speed and sorted in descending match priority * - iow, descending speed. */ static const struct phy_setting settings[] = { { .speed = SPEED_10000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10000baseKR_Full, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10000baseKX4_Full, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10000baseT_Full, }, { .speed = SPEED_2500, .duplex = DUPLEX_FULL, .setting = SUPPORTED_2500baseX_Full, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_1000baseKX_Full, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, .setting = SUPPORTED_1000baseT_Full, }, { .speed = SPEED_1000, .duplex = DUPLEX_HALF, .setting = SUPPORTED_1000baseT_Half, }, { .speed = SPEED_100, .duplex = DUPLEX_FULL, .setting = SUPPORTED_100baseT_Full, }, { .speed = SPEED_100, .duplex = DUPLEX_HALF, .setting = SUPPORTED_100baseT_Half, }, { .speed = SPEED_10, .duplex = DUPLEX_FULL, .setting = SUPPORTED_10baseT_Full, }, { .speed = SPEED_10, .duplex = DUPLEX_HALF, .setting = SUPPORTED_10baseT_Half, }, }; /** * phy_lookup_setting - lookup a PHY setting * @speed: speed to match * @duplex: duplex to match * @features: allowed link modes * @exact: an exact match is required * * Search the settings array for a setting that matches the speed and * duplex, and which is supported. * * If @exact is unset, either an exact match or %NULL for no match will * be returned. * * If @exact is set, an exact match, the fastest supported setting at * or below the specified speed, the slowest supported setting, or if * they all fail, %NULL will be returned. */ static const struct phy_setting * phy_lookup_setting(int speed, int duplex, u32 features, bool exact) { const struct phy_setting *p, *match = NULL, *last = NULL; int i; for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { if (p->setting & features) { last = p; if (p->speed == speed && p->duplex == duplex) { /* Exact match for speed and duplex */ match = p; break; } else if (!exact) { if (!match && p->speed <= speed) /* Candidate */ match = p; if (p->speed < speed) break; } } } if (!match && !exact) match = last; return match; } /** * phy_find_valid - find a PHY setting that matches the requested parameters * @speed: desired speed * @duplex: desired duplex * @supported: mask of supported link modes * * Locate a supported phy setting that is, in priority order: * - an exact match for the specified speed and duplex mode * - a match for the specified speed, or slower speed * - the slowest supported speed * Returns the matched phy_setting entry, or %NULL if no supported phy * settings were found. */ static const struct phy_setting * phy_find_valid(int speed, int duplex, u32 supported) { return phy_lookup_setting(speed, duplex, supported, false); } /** * phy_supported_speeds - return all speeds currently supported by a phy device * @phy: The phy device to return supported speeds of. * @speeds: buffer to store supported speeds in. * @size: size of speeds buffer. * * Description: Returns the number of supported speeds, and fills the speeds * buffer with the supported speeds. If speeds buffer is too small to contain * all currently supported speeds, will return as many speeds as can fit. */ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int *speeds, unsigned int size) { unsigned int count = 0; unsigned int idx = 0; for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) /* Assumes settings are grouped by speed */ if ((settings[idx].setting & phy->supported) && (count == 0 || speeds[count - 1] != settings[idx].speed)) speeds[count++] = settings[idx].speed; return count; } /** * phy_check_valid - check if there is a valid PHY setting which matches * speed, duplex, and feature mask * @speed: speed to match * @duplex: duplex to match * @features: A mask of the valid settings * * Description: Returns true if there is a valid setting, false otherwise. */ static inline bool phy_check_valid(int speed, int duplex, u32 features) { return !!phy_lookup_setting(speed, duplex, features, true); } /** * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex * @phydev: the target phy_device struct * * Description: Make sure the PHY is set to supported speeds and * duplexes. Drop down by one in this order: 1000/FULL, * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. */ static void phy_sanitize_settings(struct phy_device *phydev) { const struct phy_setting *setting; u32 features = phydev->supported; /* Sanitize settings based on PHY capabilities */ if ((features & SUPPORTED_Autoneg) == 0) phydev->autoneg = AUTONEG_DISABLE; setting = phy_find_valid(phydev->speed, phydev->duplex, features); if (setting) { phydev->speed = setting->speed; phydev->duplex = setting->duplex; } else { /* We failed to find anything (no supported speeds?) */ phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; } } /** * phy_ethtool_sset - generic ethtool sset function, handles all the details * @phydev: target phy_device struct * @cmd: ethtool_cmd * * A few notes about parameter checking: * * - We don't set port or transceiver, so we don't care what they * were set to. * - phy_start_aneg() will make sure forced settings are sane, and * choose the next best ones from the ones selected, so we don't * care if ethtool tries to give us bad values. */ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) { u32 speed = ethtool_cmd_speed(cmd); if (cmd->phy_address != phydev->mdio.addr) return -EINVAL; /* We make sure that we don't pass unsupported values in to the PHY */ cmd->advertising &= phydev->supported; /* Verify the settings we care about. */ if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE) return -EINVAL; if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0) return -EINVAL; if (cmd->autoneg == AUTONEG_DISABLE && ((speed != SPEED_1000 && speed != SPEED_100 && speed != SPEED_10) || (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL))) return -EINVAL; phydev->autoneg = cmd->autoneg; phydev->speed = speed; phydev->advertising = cmd->advertising; if (AUTONEG_ENABLE == cmd->autoneg) phydev->advertising |= ADVERTISED_Autoneg; else phydev->advertising &= ~ADVERTISED_Autoneg; phydev->duplex = cmd->duplex; phydev->mdix_ctrl = cmd->eth_tp_mdix_ctrl; /* Restart the PHY */ phy_start_aneg(phydev); return 0; } EXPORT_SYMBOL(phy_ethtool_sset); int phy_ethtool_ksettings_set(struct phy_device *phydev, const struct ethtool_link_ksettings *cmd) { u8 autoneg = cmd->base.autoneg; u8 duplex = cmd->base.duplex; u32 speed = cmd->base.speed; u32 advertising; if (cmd->base.phy_address != phydev->mdio.addr) return -EINVAL; ethtool_convert_link_mode_to_legacy_u32(&advertising, cmd->link_modes.advertising); /* We make sure that we don't pass unsupported values in to the PHY */ advertising &= phydev->supported; /* Verify the settings we care about. */ if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE) return -EINVAL; if (autoneg == AUTONEG_ENABLE && advertising == 0) return -EINVAL; if (autoneg == AUTONEG_DISABLE && ((speed != SPEED_1000 && speed != SPEED_100 && speed != SPEED_10) || (duplex != DUPLEX_HALF && duplex != DUPLEX_FULL))) return -EINVAL; phydev->autoneg = autoneg; phydev->speed = speed; phydev->advertising = advertising; if (autoneg == AUTONEG_ENABLE) phydev->advertising |= ADVERTISED_Autoneg;