903 lines · 24.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * Core PHY library, taken from phy.c */#include <linux/export.h>#include <linux/phy.h>#include <linux/phy_port.h>#include <linux/of.h> #include "phylib.h"#include "phylib-internal.h"#include "phy-caps.h" /** * phy_speed_to_str - Return a string representing the PHY link speed * * @speed: Speed of the link */const char *phy_speed_to_str(int speed){	BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 125,		"Enum ethtool_link_mode_bit_indices and phylib are out of sync. "		"If a speed or mode has been added please update phy_speed_to_str "		"and the PHY settings array.\n"); 	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_80000:		return "80Gbps";	case SPEED_100000:		return "100Gbps";	case SPEED_200000:		return "200Gbps";	case SPEED_400000:		return "400Gbps";	case SPEED_800000:		return "800Gbps";	case SPEED_1600000:		return "1600Gbps";	case SPEED_UNKNOWN:		return "Unknown";	default:		return "Unsupported (update phy-core.c)";	}}EXPORT_SYMBOL_GPL(phy_speed_to_str); /** * phy_duplex_to_str - Return string describing the duplex * * @duplex: Duplex setting to describe */const char *phy_duplex_to_str(unsigned int duplex){	if (duplex == DUPLEX_HALF)		return "Half";	if (duplex == DUPLEX_FULL)		return "Full";	if (duplex == DUPLEX_UNKNOWN)		return "Unknown";	return "Unsupported (update phy-core.c)";}EXPORT_SYMBOL_GPL(phy_duplex_to_str); /** * phy_rate_matching_to_str - Return a string describing the rate matching * * @rate_matching: Type of rate matching to describe */const char *phy_rate_matching_to_str(int rate_matching){	switch (rate_matching) {	case RATE_MATCH_NONE:		return "none";	case RATE_MATCH_PAUSE:		return "pause";	case RATE_MATCH_CRS:		return "crs";	case RATE_MATCH_OPEN_LOOP:		return "open-loop";	}	return "Unsupported (update phy-core.c)";}EXPORT_SYMBOL_GPL(phy_rate_matching_to_str); /** * phy_fix_phy_mode_for_mac_delays - Convenience function for fixing PHY * mode based on whether mac adds internal delay * * @interface: The current interface mode of the port * @mac_txid: True if the mac adds internal tx delay * @mac_rxid: True if the mac adds internal rx delay * * Return: fixed PHY mode, or PHY_INTERFACE_MODE_NA if the interface can * not apply the internal delay */phy_interface_t phy_fix_phy_mode_for_mac_delays(phy_interface_t interface,						bool mac_txid, bool mac_rxid){	if (!phy_interface_mode_is_rgmii(interface))		return interface; 	if (mac_txid && mac_rxid) {		if (interface == PHY_INTERFACE_MODE_RGMII_ID)			return PHY_INTERFACE_MODE_RGMII;		return PHY_INTERFACE_MODE_NA;	} 	if (mac_txid) {		if (interface == PHY_INTERFACE_MODE_RGMII_ID)			return PHY_INTERFACE_MODE_RGMII_RXID;		if (interface == PHY_INTERFACE_MODE_RGMII_TXID)			return PHY_INTERFACE_MODE_RGMII;		return PHY_INTERFACE_MODE_NA;	} 	if (mac_rxid) {		if (interface == PHY_INTERFACE_MODE_RGMII_ID)			return PHY_INTERFACE_MODE_RGMII_TXID;		if (interface == PHY_INTERFACE_MODE_RGMII_RXID)			return PHY_INTERFACE_MODE_RGMII;		return PHY_INTERFACE_MODE_NA;	} 	return interface;}EXPORT_SYMBOL_GPL(phy_fix_phy_mode_for_mac_delays); /** * phy_interface_num_ports - Return the number of links that can be carried by *			     a given MAC-PHY physical link. Returns 0 if this is *			     unknown, the number of links else. * * @interface: The interface mode we want to get the number of ports */int phy_interface_num_ports(phy_interface_t interface){	switch (interface) {	case PHY_INTERFACE_MODE_NA:		return 0;	case PHY_INTERFACE_MODE_INTERNAL:	case PHY_INTERFACE_MODE_MII:	case PHY_INTERFACE_MODE_MIILITE:	case PHY_INTERFACE_MODE_GMII:	case PHY_INTERFACE_MODE_TBI:	case PHY_INTERFACE_MODE_REVMII:	case PHY_INTERFACE_MODE_RMII:	case PHY_INTERFACE_MODE_REVRMII:	case PHY_INTERFACE_MODE_RGMII:	case PHY_INTERFACE_MODE_RGMII_ID:	case PHY_INTERFACE_MODE_RGMII_RXID:	case PHY_INTERFACE_MODE_RGMII_TXID:	case PHY_INTERFACE_MODE_RTBI:	case PHY_INTERFACE_MODE_XGMII:	case PHY_INTERFACE_MODE_XLGMII:	case PHY_INTERFACE_MODE_MOCA:	case PHY_INTERFACE_MODE_TRGMII:	case PHY_INTERFACE_MODE_USXGMII:	case PHY_INTERFACE_MODE_SGMII:	case PHY_INTERFACE_MODE_SMII:	case PHY_INTERFACE_MODE_1000BASEX:	case PHY_INTERFACE_MODE_2500BASEX:	case PHY_INTERFACE_MODE_5GBASER:	case PHY_INTERFACE_MODE_10GBASER:	case PHY_INTERFACE_MODE_25GBASER:	case PHY_INTERFACE_MODE_10GKR:	case PHY_INTERFACE_MODE_100BASEX:	case PHY_INTERFACE_MODE_RXAUI:	case PHY_INTERFACE_MODE_XAUI:	case PHY_INTERFACE_MODE_1000BASEKX:	case PHY_INTERFACE_MODE_50GBASER:	case PHY_INTERFACE_MODE_LAUI:	case PHY_INTERFACE_MODE_100GBASEP:		return 1;	case PHY_INTERFACE_MODE_QSGMII:	case PHY_INTERFACE_MODE_QUSGMII:	case PHY_INTERFACE_MODE_10G_QXGMII:		return 4;	case PHY_INTERFACE_MODE_PSGMII:		return 5;	case PHY_INTERFACE_MODE_MAX:		WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");		return 0;	}	return 0;}EXPORT_SYMBOL_GPL(phy_interface_num_ports); static void __set_phy_supported(struct phy_device *phydev, u32 max_speed){	struct phy_port *port; 	phy_caps_linkmode_max_speed(max_speed, phydev->supported); 	phy_for_each_port(phydev, port)		phy_caps_linkmode_max_speed(max_speed, port->supported);} /** * phy_set_max_speed - Set the maximum speed the PHY should support * * @phydev: The phy_device struct * @max_speed: Maximum speed * * The PHY might be more capable than the MAC. For example a Fast Ethernet * is connected to a 1G PHY. This function allows the MAC to indicate its * maximum speed, and so limit what the PHY will advertise. */void phy_set_max_speed(struct phy_device *phydev, u32 max_speed){	__set_phy_supported(phydev, max_speed); 	phy_advertise_supported(phydev);}EXPORT_SYMBOL(phy_set_max_speed); void of_set_phy_supported(struct phy_device *phydev){	struct device_node *node = phydev->mdio.dev.of_node;	u32 max_speed; 	if (!IS_ENABLED(CONFIG_OF_MDIO))		return; 	if (!node)		return; 	if (!of_property_read_u32(node, "max-speed", &max_speed))		__set_phy_supported(phydev, max_speed);} void of_set_phy_eee_broken(struct phy_device *phydev){	struct device_node *node = phydev->mdio.dev.of_node;	unsigned long *modes = phydev->eee_disabled_modes; 	if (!IS_ENABLED(CONFIG_OF_MDIO) || !node)		return; 	linkmode_zero(modes); 	if (of_property_read_bool(node, "eee-broken-100tx"))		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, modes);	if (of_property_read_bool(node, "eee-broken-1000t"))		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, modes);	if (of_property_read_bool(node, "eee-broken-10gt"))		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, modes);	if (of_property_read_bool(node, "eee-broken-1000kx"))		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, modes);	if (of_property_read_bool(node, "eee-broken-10gkx4"))		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, modes);	if (of_property_read_bool(node, "eee-broken-10gkr"))		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, modes);} /** * of_set_phy_timing_role - Set the master/slave mode of the PHY * * @phydev: The phy_device struct * * Set master/slave configuration of the PHY based on the device tree. */void of_set_phy_timing_role(struct phy_device *phydev){	struct device_node *node = phydev->mdio.dev.of_node;	const char *master; 	if (!IS_ENABLED(CONFIG_OF_MDIO))		return; 	if (!node)		return; 	if (of_property_read_string(node, "timing-role", &master))		return; 	if (strcmp(master, "forced-master") == 0)		phydev->master_slave_set = MASTER_SLAVE_CFG_MASTER_FORCE;	else if (strcmp(master, "forced-slave") == 0)		phydev->master_slave_set = MASTER_SLAVE_CFG_SLAVE_FORCE;	else if (strcmp(master, "preferred-master") == 0)		phydev->master_slave_set = MASTER_SLAVE_CFG_MASTER_PREFERRED;	else if (strcmp(master, "preferred-slave") == 0)		phydev->master_slave_set = MASTER_SLAVE_CFG_SLAVE_PREFERRED;	else		phydev_warn(phydev, "Unknown master-slave mode %s\n", master);} /** * phy_resolve_aneg_pause - Determine pause autoneg results * * @phydev: The phy_device struct * * Once autoneg has completed the local pause settings can be * resolved.  Determine if pause and asymmetric pause should be used * by the MAC. */ void phy_resolve_aneg_pause(struct phy_device *phydev){	if (phydev->duplex == DUPLEX_FULL) {		phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,						  phydev->lp_advertising);		phydev->asym_pause = linkmode_test_bit(			ETHTOOL_LINK_MODE_Asym_Pause_BIT,			phydev->lp_advertising);	}}EXPORT_SYMBOL_GPL(phy_resolve_aneg_pause); /** * phy_resolve_aneg_linkmode - resolve the advertisements into PHY settings * @phydev: The phy_device struct * * Resolve our and the link partner advertisements into their corresponding * speed and duplex. If full duplex was negotiated, extract the pause mode * from the link partner mask. */void phy_resolve_aneg_linkmode(struct phy_device *phydev){	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);	const struct link_capabilities *c; 	linkmode_and(common, phydev->lp_advertising, phydev->advertising); 	c = phy_caps_lookup_by_linkmode(common);	if (c) {		phydev->speed = c->speed;		phydev->duplex = c->duplex;	} 	phy_resolve_aneg_pause(phydev);}EXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode); /** * phy_check_downshift - check whether downshift occurred * @phydev: The phy_device struct * * Check whether a downshift to a lower speed occurred. If this should be the * case warn the user. * Prerequisite for detecting downshift is that PHY driver implements the * read_status callback and sets phydev->speed to the actual link speed. */void phy_check_downshift(struct phy_device *phydev){	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);	const struct link_capabilities *c;	int speed = SPEED_UNKNOWN; 	phydev->downshifted_rate = 0; 	if (phydev->autoneg == AUTONEG_DISABLE ||	    phydev->speed == SPEED_UNKNOWN)		return; 	linkmode_and(common, phydev->lp_advertising, phydev->advertising); 	c = phy_caps_lookup_by_linkmode(common);	if (c)		speed = c->speed; 	if (speed == SPEED_UNKNOWN || phydev->speed >= speed)		return; 	phydev_warn(phydev, "Downshift occurred from negotiated speed %s to actual speed %s, check cabling!\n",		    phy_speed_to_str(speed), phy_speed_to_str(phydev->speed)); 	phydev->downshifted_rate = 1;} static int phy_resolve_min_speed(struct phy_device *phydev, bool fdx_only){	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);	const struct link_capabilities *c; 	linkmode_and(common, phydev->lp_advertising, phydev->advertising); 	c = phy_caps_lookup_by_linkmode_rev(common, fdx_only);	if (c)		return c->speed; 	return SPEED_UNKNOWN;} int phy_speed_down_core(struct phy_device *phydev){	int min_common_speed = phy_resolve_min_speed(phydev, true); 	if (min_common_speed == SPEED_UNKNOWN)		return -EINVAL; 	phy_caps_linkmode_max_speed(min_common_speed, phydev->advertising); 	return 0;} static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,			     u16 regnum){	/* Write the desired MMD Devad */	__mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad); 	/* Write the desired MMD register address */	__mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum); 	/* Select the Function : DATA with no post increment */	__mdiobus_write(bus, phy_addr, MII_MMD_CTRL,			devad | MII_MMD_CTRL_NOINCR);} int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,		 int devad, u32 regnum){	if (is_c45)		return __mdiobus_c45_read(bus, phy_addr, devad, regnum); 	mmd_phy_indirect(bus, phy_addr, devad, regnum);	/* Read the content of the MMD's selected register */	return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);}EXPORT_SYMBOL_GPL(mmd_phy_read); int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,		  int devad, u32 regnum, u16 val){	if (is_c45)		return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val); 	mmd_phy_indirect(bus, phy_addr, devad, regnum);	/* Write the data into MMD's selected register */	return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);}EXPORT_SYMBOL_GPL(mmd_phy_write); /** * __phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. * @phydev: The phy_device struct * @devad: The MMD to read from (0..31) * @regnum: The register on the MMD to read (0..65535) * * Same rules as for __phy_read(); */int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum){	if (regnum > (u16)~0 || devad > 32)		return -EINVAL; 	if (phydev->drv && phydev->drv->read_mmd)		return phydev->drv->read_mmd(phydev, devad, regnum); 	return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr,			    phydev->is_c45, devad, regnum);}EXPORT_SYMBOL(__phy_read_mmd); /** * phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. * @phydev: The phy_device struct * @devad: The MMD to read from * @regnum: The register on the MMD to read * * Same rules as for phy_read(); */int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum){	int ret; 	phy_lock_mdio_bus(phydev);	ret = __phy_read_mmd(phydev, devad, regnum);	phy_unlock_mdio_bus(phydev); 	return ret;}EXPORT_SYMBOL(phy_read_mmd);

Lines 1–500 of 903.