1448 lines · 35.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* Framework for configuring and reading PHY devices * Based on code in sungem_phy.c and gianfar_phy.c * * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. * Copyright (c) 2006, 2007  Maciej W. Rozycki */ #include <linux/kernel.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/unistd.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/netlink.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/ethtool_netlink.h>#include <linux/phy.h>#include <linux/phy_led_triggers.h>#include <linux/sfp.h>#include <linux/workqueue.h>#include <linux/mdio.h>#include <linux/io.h>#include <linux/uaccess.h>#include <linux/atomic.h>#include <net/netlink.h>#include <net/genetlink.h>#include <net/sock.h> #define PHY_STATE_TIME	HZ #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(READY)	PHY_STATE_STR(UP)	PHY_STATE_STR(RUNNING)	PHY_STATE_STR(NOLINK)	PHY_STATE_STR(CABLETEST)	PHY_STATE_STR(HALTED)	} 	return NULL;} static void phy_link_up(struct phy_device *phydev){	phydev->phy_link_change(phydev, true);	phy_led_trigger_change_speed(phydev);} static void phy_link_down(struct phy_device *phydev){	phydev->phy_link_change(phydev, false);	phy_led_trigger_change_speed(phydev);} static const char *phy_pause_str(struct phy_device *phydev){	bool local_pause, local_asym_pause; 	if (phydev->autoneg == AUTONEG_DISABLE)		goto no_pause; 	local_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,					phydev->advertising);	local_asym_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,					     phydev->advertising); 	if (local_pause && phydev->pause)		return "rx/tx"; 	if (local_asym_pause && phydev->asym_pause) {		if (local_pause)			return "rx";		if (phydev->pause)			return "tx";	} no_pause:	return "off";} /** * 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 %s- flow control %s\n",			phy_speed_to_str(phydev->speed),			phy_duplex_to_str(phydev->duplex),			phydev->downshifted_rate ? "(downshifted) " : "",			phy_pause_str(phydev));	} else	{		netdev_info(phydev->attached_dev, "Link is Down\n");	}}EXPORT_SYMBOL(phy_print_status); /** * phy_config_interrupt - configure the PHY device for the requested interrupts * @phydev: the phy_device struct * @interrupts: interrupt flags to configure for this @phydev * * Returns 0 on success or < 0 on error. */static int phy_config_interrupt(struct phy_device *phydev, bool interrupts){	phydev->interrupts = interrupts ? 1 : 0;	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);	else if (phydev->is_c45)		return genphy_c45_aneg_done(phydev);	else		return genphy_aneg_done(phydev);}EXPORT_SYMBOL(phy_aneg_done); /** * phy_find_valid - find a PHY setting that matches the requested parameters * @speed: desired speed * @duplex: desired duplex * @supported: mask of supported link modes * * Locate a supported phy setting that is, in priority order: * - an exact match for the specified speed and duplex mode * - a match for the specified speed, or slower speed * - the slowest supported speed * Returns the matched phy_setting entry, or %NULL if no supported phy * settings were found. */static const struct phy_setting *phy_find_valid(int speed, int duplex, unsigned long *supported){	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){	return phy_speeds(speeds, size, phy->supported);} /** * 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,				   unsigned long *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; 	setting = phy_find_valid(phydev->speed, phydev->duplex,				 phydev->supported);	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;	}} int phy_ethtool_ksettings_set(struct phy_device *phydev,			      const struct ethtool_link_ksettings *cmd){	__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);	u8 autoneg = cmd->base.autoneg;	u8 duplex = cmd->base.duplex;	u32 speed = cmd->base.speed; 	if (cmd->base.phy_address != phydev->mdio.addr)		return -EINVAL; 	linkmode_copy(advertising, cmd->link_modes.advertising); 	/* We make sure that we don't pass unsupported values in to the PHY */	linkmode_and(advertising, advertising, phydev->supported); 	/* Verify the settings we care about. */	if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE)		return -EINVAL; 	if (autoneg == AUTONEG_ENABLE && linkmode_empty(advertising))		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; 	if (autoneg == AUTONEG_DISABLE) {		phydev->speed = speed;		phydev->duplex = duplex;	} 	linkmode_copy(phydev->advertising, advertising); 	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,			 phydev->advertising, autoneg == AUTONEG_ENABLE); 	phydev->master_slave_set = cmd->base.master_slave_cfg;	phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl; 	/* Restart the PHY */	phy_start_aneg(phydev); 	return 0;}EXPORT_SYMBOL(phy_ethtool_ksettings_set); void phy_ethtool_ksettings_get(struct phy_device *phydev,			       struct ethtool_link_ksettings *cmd){	linkmode_copy(cmd->link_modes.supported, phydev->supported);	linkmode_copy(cmd->link_modes.advertising, phydev->advertising);	linkmode_copy(cmd->link_modes.lp_advertising, phydev->lp_advertising); 	cmd->base.speed = phydev->speed;	cmd->base.duplex = phydev->duplex;	cmd->base.master_slave_cfg = phydev->master_slave_get;	cmd->base.master_slave_state = phydev->master_slave_state;	if (phydev->interface == PHY_INTERFACE_MODE_MOCA)		cmd->base.port = PORT_BNC;	else		cmd->base.port = phydev->port;	cmd->base.transceiver = phy_is_internal(phydev) ?				XCVR_INTERNAL : XCVR_EXTERNAL;	cmd->base.phy_address = phydev->mdio.addr;	cmd->base.autoneg = phydev->autoneg;	cmd->base.eth_tp_mdix_ctrl = phydev->mdix_ctrl;	cmd->base.eth_tp_mdix = phydev->mdix;}EXPORT_SYMBOL(phy_ethtool_ksettings_get); /** * phy_mii_ioctl - generic PHY MII ioctl interface * @phydev: the phy_device struct * @ifr: &struct ifreq for socket ioctl's * @cmd: ioctl cmd to execute * * Note that this function is currently incompatible with the * PHYCONTROL layer.  It changes registers without regard to * current state.  Use at own risk. */int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd){	struct mii_ioctl_data *mii_data = if_mii(ifr);	u16 val = mii_data->val_in;	bool change_autoneg = false;	int prtad, devad; 	switch (cmd) {	case SIOCGMIIPHY:		mii_data->phy_id = phydev->mdio.addr;		fallthrough; 	case SIOCGMIIREG:		if (mdio_phy_id_is_c45(mii_data->phy_id)) {			prtad = mdio_phy_id_prtad(mii_data->phy_id);			devad = mdio_phy_id_devad(mii_data->phy_id);			devad = mdiobus_c45_addr(devad, mii_data->reg_num);		} else {			prtad = mii_data->phy_id;			devad = mii_data->reg_num;		}		mii_data->val_out = mdiobus_read(phydev->mdio.bus, prtad,						 devad);		return 0; 	case SIOCSMIIREG:		if (mdio_phy_id_is_c45(mii_data->phy_id)) {			prtad = mdio_phy_id_prtad(mii_data->phy_id);			devad = mdio_phy_id_devad(mii_data->phy_id);			devad = mdiobus_c45_addr(devad, mii_data->reg_num);		} else {			prtad = mii_data->phy_id;			devad = mii_data->reg_num;		}		if (prtad == phydev->mdio.addr) {			switch (devad) {			case MII_BMCR:				if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {					if (phydev->autoneg == AUTONEG_ENABLE)						change_autoneg = true;					phydev->autoneg = AUTONEG_DISABLE;					if (val & BMCR_FULLDPLX)						phydev->duplex = DUPLEX_FULL;					else						phydev->duplex = DUPLEX_HALF;					if (val & BMCR_SPEED1000)						phydev->speed = SPEED_1000;					else if (val & BMCR_SPEED100)						phydev->speed = SPEED_100;					else phydev->speed = SPEED_10;				}				else {					if (phydev->autoneg == AUTONEG_DISABLE)						change_autoneg = true;					phydev->autoneg = AUTONEG_ENABLE;				}				break;			case MII_ADVERTISE:				mii_adv_mod_linkmode_adv_t(phydev->advertising,							   val);				change_autoneg = true;				break;			case MII_CTRL1000:				mii_ctrl1000_mod_linkmode_adv_t(phydev->advertising,							        val);				change_autoneg = true;				break;			default:				/* do nothing */				break;			}		} 		mdiobus_write(phydev->mdio.bus, prtad, devad, val); 		if (prtad == phydev->mdio.addr &&		    devad == MII_BMCR &&		    val & BMCR_RESET)			return phy_init_hw(phydev); 		if (change_autoneg)			return phy_start_aneg(phydev); 		return 0; 	case SIOCSHWTSTAMP:		if (phydev->mii_ts && phydev->mii_ts->hwtstamp)			return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr);		fallthrough; 	default:		return -EOPNOTSUPP;	}}EXPORT_SYMBOL(phy_mii_ioctl); /** * phy_do_ioctl - generic ndo_do_ioctl implementation * @dev: the net_device struct * @ifr: &struct ifreq for socket ioctl's * @cmd: ioctl cmd to execute */int phy_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){	if (!dev->phydev)		return -ENODEV; 	return phy_mii_ioctl(dev->phydev, ifr, cmd);}EXPORT_SYMBOL(phy_do_ioctl); /** * phy_do_ioctl_running - generic ndo_do_ioctl implementation but test first * * @dev: the net_device struct * @ifr: &struct ifreq for socket ioctl's * @cmd: ioctl cmd to execute * * Same as phy_do_ioctl, but ensures that net_device is running before * handling the ioctl. */int phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd){	if (!netif_running(dev))		return -ENODEV; 	return phy_do_ioctl(dev, ifr, cmd);}EXPORT_SYMBOL(phy_do_ioctl_running); /** * phy_queue_state_machine - Trigger the state machine to run soon * * @phydev: the phy_device struct * @jiffies: Run the state machine after these jiffies */void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies){	mod_delayed_work(system_power_efficient_wq, &phydev->state_queue,			 jiffies);}EXPORT_SYMBOL(phy_queue_state_machine); /** * phy_trigger_machine - Trigger the state machine to run now * * @phydev: the phy_device struct */void phy_trigger_machine(struct phy_device *phydev){	phy_queue_state_machine(phydev, 0);}EXPORT_SYMBOL(phy_trigger_machine); static void phy_abort_cable_test(struct phy_device *phydev){	int err; 	ethnl_cable_test_finished(phydev); 	err = phy_init_hw(phydev);	if (err)		phydev_err(phydev, "Error while aborting cable test");} /** * phy_ethtool_get_strings - Get the statistic counter names *

Lines 1–500 of 1448.