lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <65b5544e.050a0220.86866.2828@mx.google.com>
Date: Sat, 27 Jan 2024 19:58:08 +0100
From: Christian Marangi <ansuelsmth@...il.com>
To: Andrew Lunn <andrew@...n.ch>, Heiner Kallweit <hkallweit1@...il.com>,
	Russell King <linux@...linux.org.uk>,
	"David S. Miller" <davem@...emloft.net>,
	Eric Dumazet <edumazet@...gle.com>,
	Jakub Kicinski <kuba@...nel.org>, Paolo Abeni <pabeni@...hat.com>,
	Bjorn Andersson <andersson@...nel.org>,
	Konrad Dybcio <konrad.dybcio@...aro.org>,
	linux-kernel@...r.kernel.org, netdev@...r.kernel.org,
	linux-arm-msm@...r.kernel.org
Subject: Re: [net-next PATCH v2 4/5] net: phy: qcom: move additional
 functions to shared library

On Sat, Jan 27, 2024 at 03:42:44PM +0100, Christian Marangi wrote:
> Move additional functions to shared library in preparation for qca808x
> PHY Family to be detached from at803x driver.
> 
> Only the shared defines are moved to the shared qcom.h header.
> 
> Signed-off-by: Christian Marangi <ansuelsmth@...il.com>
> ---
>  drivers/net/phy/qcom/at803x.c       | 428 +---------------------------
>  drivers/net/phy/qcom/qcom-phy-lib.c | 376 ++++++++++++++++++++++++
>  drivers/net/phy/qcom/qcom.h         |  84 ++++++
>  3 files changed, 463 insertions(+), 425 deletions(-)
> 
> diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c
> index 638babc50df1..442060793854 100644
> --- a/drivers/net/phy/qcom/at803x.c
> +++ b/drivers/net/phy/qcom/at803x.c
> @@ -24,65 +24,11 @@
>  
>  #include "qcom.h"
>  
> -#define AT803X_SPECIFIC_FUNCTION_CONTROL	0x10
> -#define AT803X_SFC_ASSERT_CRS			BIT(11)
> -#define AT803X_SFC_FORCE_LINK			BIT(10)
> -#define AT803X_SFC_MDI_CROSSOVER_MODE_M		GENMASK(6, 5)
> -#define AT803X_SFC_AUTOMATIC_CROSSOVER		0x3
> -#define AT803X_SFC_MANUAL_MDIX			0x1
> -#define AT803X_SFC_MANUAL_MDI			0x0
> -#define AT803X_SFC_SQE_TEST			BIT(2)
> -#define AT803X_SFC_POLARITY_REVERSAL		BIT(1)
> -#define AT803X_SFC_DISABLE_JABBER		BIT(0)
> -
> -#define AT803X_SPECIFIC_STATUS			0x11
> -#define AT803X_SS_SPEED_MASK			GENMASK(15, 14)
> -#define AT803X_SS_SPEED_1000			2
> -#define AT803X_SS_SPEED_100			1
> -#define AT803X_SS_SPEED_10			0
> -#define AT803X_SS_DUPLEX			BIT(13)
> -#define AT803X_SS_SPEED_DUPLEX_RESOLVED		BIT(11)
> -#define AT803X_SS_MDIX				BIT(6)
> -
> -#define QCA808X_SS_SPEED_MASK			GENMASK(9, 7)
> -#define QCA808X_SS_SPEED_2500			4

This was an intended remove and should be removed in patch 5 instead and
cause build fail on testing this single patch.

Will fix in v3, sorry for the noise!

> -
> -#define AT803X_INTR_ENABLE			0x12
> -#define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
> -#define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
> -#define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)
> -#define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)
> -#define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)
> -#define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)
> -#define AT803X_INTR_ENABLE_LINK_FAIL_BX		BIT(8)
> -#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX	BIT(7)
> -#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)
> -#define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)
> -#define AT803X_INTR_ENABLE_WOL			BIT(0)
> -
> -#define AT803X_INTR_STATUS			0x13
> -
> -#define AT803X_SMART_SPEED			0x14
> -#define AT803X_SMART_SPEED_ENABLE		BIT(5)
> -#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK	GENMASK(4, 2)
> -#define AT803X_SMART_SPEED_BYPASS_TIMER		BIT(1)
> -#define AT803X_CDT				0x16
> -#define AT803X_CDT_MDI_PAIR_MASK		GENMASK(9, 8)
> -#define AT803X_CDT_ENABLE_TEST			BIT(0)
> -#define AT803X_CDT_STATUS			0x1c
> -#define AT803X_CDT_STATUS_STAT_NORMAL		0
> -#define AT803X_CDT_STATUS_STAT_SHORT		1
> -#define AT803X_CDT_STATUS_STAT_OPEN		2
> -#define AT803X_CDT_STATUS_STAT_FAIL		3
> -#define AT803X_CDT_STATUS_STAT_MASK		GENMASK(9, 8)
> -#define AT803X_CDT_STATUS_DELTA_TIME_MASK	GENMASK(7, 0)
>  #define AT803X_LED_CONTROL			0x18
>  
>  #define AT803X_PHY_MMD3_WOL_CTRL		0x8012
>  #define AT803X_WOL_EN				BIT(5)
> -#define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
> -#define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
> -#define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
> +
>  #define AT803X_REG_CHIP_CONFIG			0x1f
>  #define AT803X_BT_BX_REG_SEL			0x8000
>  
> @@ -138,10 +84,6 @@
>  #define AT803X_CLK_OUT_STRENGTH_HALF		1
>  #define AT803X_CLK_OUT_STRENGTH_QUARTER		2
>  
> -#define AT803X_DEFAULT_DOWNSHIFT		5
> -#define AT803X_MIN_DOWNSHIFT			2
> -#define AT803X_MAX_DOWNSHIFT			9
> -
>  #define AT803X_MMD3_SMARTEEE_CTL1		0x805b
>  #define AT803X_MMD3_SMARTEEE_CTL2		0x805c
>  #define AT803X_MMD3_SMARTEEE_CTL3		0x805d
> @@ -158,6 +100,8 @@
>  
>  #define QCA9561_PHY_ID				0x004dd042
>  
> +#define AT803X_SS_SPEED_MASK			GENMASK(15, 14)
> +
>  #define AT803X_PAGE_FIBER			0
>  #define AT803X_PAGE_COPPER			1
>  
> @@ -366,11 +310,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
>  MODULE_AUTHOR("Matus Ujhelyi");
>  MODULE_LICENSE("GPL");
>  
> -struct at803x_ss_mask {
> -	u16 speed_mask;
> -	u8 speed_shift;
> -};
> -
>  struct at803x_priv {
>  	int flags;
>  	u16 clk_25m_reg;
> @@ -470,80 +409,6 @@ static void at803x_context_restore(struct phy_device *phydev,
>  	phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
>  }
>  
> -static int at803x_set_wol(struct phy_device *phydev,
> -			  struct ethtool_wolinfo *wol)
> -{
> -	int ret, irq_enabled;
> -
> -	if (wol->wolopts & WAKE_MAGIC) {
> -		struct net_device *ndev = phydev->attached_dev;
> -		const u8 *mac;
> -		unsigned int i;
> -		static const unsigned int offsets[] = {
> -			AT803X_LOC_MAC_ADDR_32_47_OFFSET,
> -			AT803X_LOC_MAC_ADDR_16_31_OFFSET,
> -			AT803X_LOC_MAC_ADDR_0_15_OFFSET,
> -		};
> -
> -		if (!ndev)
> -			return -ENODEV;
> -
> -		mac = (const u8 *)ndev->dev_addr;
> -
> -		if (!is_valid_ether_addr(mac))
> -			return -EINVAL;
> -
> -		for (i = 0; i < 3; i++)
> -			phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
> -				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
> -
> -		/* Enable WOL interrupt */
> -		ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
> -		if (ret)
> -			return ret;
> -	} else {
> -		/* Disable WOL interrupt */
> -		ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	/* Clear WOL status */
> -	ret = phy_read(phydev, AT803X_INTR_STATUS);
> -	if (ret < 0)
> -		return ret;
> -
> -	/* Check if there are other interrupts except for WOL triggered when PHY is
> -	 * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
> -	 * be passed up to the interrupt PIN.
> -	 */
> -	irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
> -	if (irq_enabled < 0)
> -		return irq_enabled;
> -
> -	irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
> -	if (ret & irq_enabled && !phy_polling_mode(phydev))
> -		phy_trigger_machine(phydev);
> -
> -	return 0;
> -}
> -
> -static void at803x_get_wol(struct phy_device *phydev,
> -			   struct ethtool_wolinfo *wol)
> -{
> -	int value;
> -
> -	wol->supported = WAKE_MAGIC;
> -	wol->wolopts = 0;
> -
> -	value = phy_read(phydev, AT803X_INTR_ENABLE);
> -	if (value < 0)
> -		return;
> -
> -	if (value & AT803X_INTR_ENABLE_WOL)
> -		wol->wolopts |= WAKE_MAGIC;
> -}
> -
>  static int at803x_suspend(struct phy_device *phydev)
>  {
>  	int value;
> @@ -816,73 +681,6 @@ static int at803x_config_init(struct phy_device *phydev)
>  	return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
>  }
>  
> -static int at803x_ack_interrupt(struct phy_device *phydev)
> -{
> -	int err;
> -
> -	err = phy_read(phydev, AT803X_INTR_STATUS);
> -
> -	return (err < 0) ? err : 0;
> -}
> -
> -static int at803x_config_intr(struct phy_device *phydev)
> -{
> -	int err;
> -	int value;
> -
> -	value = phy_read(phydev, AT803X_INTR_ENABLE);
> -
> -	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
> -		/* Clear any pending interrupts */
> -		err = at803x_ack_interrupt(phydev);
> -		if (err)
> -			return err;
> -
> -		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
> -		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
> -		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
> -		value |= AT803X_INTR_ENABLE_LINK_FAIL;
> -		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
> -
> -		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
> -	} else {
> -		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
> -		if (err)
> -			return err;
> -
> -		/* Clear any pending interrupts */
> -		err = at803x_ack_interrupt(phydev);
> -	}
> -
> -	return err;
> -}
> -
> -static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
> -{
> -	int irq_status, int_enabled;
> -
> -	irq_status = phy_read(phydev, AT803X_INTR_STATUS);
> -	if (irq_status < 0) {
> -		phy_error(phydev);
> -		return IRQ_NONE;
> -	}
> -
> -	/* Read the current enabled interrupts */
> -	int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
> -	if (int_enabled < 0) {
> -		phy_error(phydev);
> -		return IRQ_NONE;
> -	}
> -
> -	/* See if this was one of our enabled interrupts */
> -	if (!(irq_status & int_enabled))
> -		return IRQ_NONE;
> -
> -	phy_trigger_machine(phydev);
> -
> -	return IRQ_HANDLED;
> -}
> -
>  static void at803x_link_change_notify(struct phy_device *phydev)
>  {
>  	/*
> @@ -908,69 +706,6 @@ static void at803x_link_change_notify(struct phy_device *phydev)
>  	}
>  }
>  
> -static int at803x_read_specific_status(struct phy_device *phydev,
> -				       struct at803x_ss_mask ss_mask)
> -{
> -	int ss;
> -
> -	/* Read the AT8035 PHY-Specific Status register, which indicates the
> -	 * speed and duplex that the PHY is actually using, irrespective of
> -	 * whether we are in autoneg mode or not.
> -	 */
> -	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
> -	if (ss < 0)
> -		return ss;
> -
> -	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
> -		int sfc, speed;
> -
> -		sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
> -		if (sfc < 0)
> -			return sfc;
> -
> -		speed = ss & ss_mask.speed_mask;
> -		speed >>= ss_mask.speed_shift;
> -
> -		switch (speed) {
> -		case AT803X_SS_SPEED_10:
> -			phydev->speed = SPEED_10;
> -			break;
> -		case AT803X_SS_SPEED_100:
> -			phydev->speed = SPEED_100;
> -			break;
> -		case AT803X_SS_SPEED_1000:
> -			phydev->speed = SPEED_1000;
> -			break;
> -		case QCA808X_SS_SPEED_2500:
> -			phydev->speed = SPEED_2500;
> -			break;
> -		}
> -		if (ss & AT803X_SS_DUPLEX)
> -			phydev->duplex = DUPLEX_FULL;
> -		else
> -			phydev->duplex = DUPLEX_HALF;
> -
> -		if (ss & AT803X_SS_MDIX)
> -			phydev->mdix = ETH_TP_MDI_X;
> -		else
> -			phydev->mdix = ETH_TP_MDI;
> -
> -		switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
> -		case AT803X_SFC_MANUAL_MDI:
> -			phydev->mdix_ctrl = ETH_TP_MDI;
> -			break;
> -		case AT803X_SFC_MANUAL_MDIX:
> -			phydev->mdix_ctrl = ETH_TP_MDI_X;
> -			break;
> -		case AT803X_SFC_AUTOMATIC_CROSSOVER:
> -			phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
> -			break;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
>  static int at803x_read_status(struct phy_device *phydev)
>  {
>  	struct at803x_ss_mask ss_mask = { 0 };
> @@ -1006,50 +741,6 @@ static int at803x_read_status(struct phy_device *phydev)
>  	return 0;
>  }
>  
> -static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
> -{
> -	u16 val;
> -
> -	switch (ctrl) {
> -	case ETH_TP_MDI:
> -		val = AT803X_SFC_MANUAL_MDI;
> -		break;
> -	case ETH_TP_MDI_X:
> -		val = AT803X_SFC_MANUAL_MDIX;
> -		break;
> -	case ETH_TP_MDI_AUTO:
> -		val = AT803X_SFC_AUTOMATIC_CROSSOVER;
> -		break;
> -	default:
> -		return 0;
> -	}
> -
> -	return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
> -			  AT803X_SFC_MDI_CROSSOVER_MODE_M,
> -			  FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
> -}
> -
> -static int at803x_prepare_config_aneg(struct phy_device *phydev)
> -{
> -	int ret;
> -
> -	ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
> -	if (ret < 0)
> -		return ret;
> -
> -	/* Changes of the midx bits are disruptive to the normal operation;
> -	 * therefore any changes to these registers must be followed by a
> -	 * software reset to take effect.
> -	 */
> -	if (ret == 1) {
> -		ret = genphy_soft_reset(phydev);
> -		if (ret < 0)
> -			return ret;
> -	}
> -
> -	return 0;
> -}
> -
>  static int at803x_config_aneg(struct phy_device *phydev)
>  {
>  	struct at803x_priv *priv = phydev->priv;
> @@ -1065,80 +756,6 @@ static int at803x_config_aneg(struct phy_device *phydev)
>  	return genphy_config_aneg(phydev);
>  }
>  
> -static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
> -{
> -	int val;
> -
> -	val = phy_read(phydev, AT803X_SMART_SPEED);
> -	if (val < 0)
> -		return val;
> -
> -	if (val & AT803X_SMART_SPEED_ENABLE)
> -		*d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
> -	else
> -		*d = DOWNSHIFT_DEV_DISABLE;
> -
> -	return 0;
> -}
> -
> -static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
> -{
> -	u16 mask, set;
> -	int ret;
> -
> -	switch (cnt) {
> -	case DOWNSHIFT_DEV_DEFAULT_COUNT:
> -		cnt = AT803X_DEFAULT_DOWNSHIFT;
> -		fallthrough;
> -	case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
> -		set = AT803X_SMART_SPEED_ENABLE |
> -		      AT803X_SMART_SPEED_BYPASS_TIMER |
> -		      FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
> -		mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
> -		break;
> -	case DOWNSHIFT_DEV_DISABLE:
> -		set = 0;
> -		mask = AT803X_SMART_SPEED_ENABLE |
> -		       AT803X_SMART_SPEED_BYPASS_TIMER;
> -		break;
> -	default:
> -		return -EINVAL;
> -	}
> -
> -	ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
> -
> -	/* After changing the smart speed settings, we need to perform a
> -	 * software reset, use phy_init_hw() to make sure we set the
> -	 * reapply any values which might got lost during software reset.
> -	 */
> -	if (ret == 1)
> -		ret = phy_init_hw(phydev);
> -
> -	return ret;
> -}
> -
> -static int at803x_get_tunable(struct phy_device *phydev,
> -			      struct ethtool_tunable *tuna, void *data)
> -{
> -	switch (tuna->id) {
> -	case ETHTOOL_PHY_DOWNSHIFT:
> -		return at803x_get_downshift(phydev, data);
> -	default:
> -		return -EOPNOTSUPP;
> -	}
> -}
> -
> -static int at803x_set_tunable(struct phy_device *phydev,
> -			      struct ethtool_tunable *tuna, const void *data)
> -{
> -	switch (tuna->id) {
> -	case ETHTOOL_PHY_DOWNSHIFT:
> -		return at803x_set_downshift(phydev, *(const u8 *)data);
> -	default:
> -		return -EOPNOTSUPP;
> -	}
> -}
> -
>  static int at803x_cable_test_result_trans(u16 status)
>  {
>  	switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
> @@ -1170,45 +787,6 @@ static bool at803x_cdt_fault_length_valid(u16 status)
>  	return false;
>  }
>  
> -static int at803x_cdt_fault_length(int dt)
> -{
> -	/* According to the datasheet the distance to the fault is
> -	 * DELTA_TIME * 0.824 meters.
> -	 *
> -	 * The author suspect the correct formula is:
> -	 *
> -	 *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
> -	 *
> -	 * where c is the speed of light, VF is the velocity factor of
> -	 * the twisted pair cable, 125MHz the counter frequency and
> -	 * we need to divide by 2 because the hardware will measure the
> -	 * round trip time to the fault and back to the PHY.
> -	 *
> -	 * With a VF of 0.69 we get the factor 0.824 mentioned in the
> -	 * datasheet.
> -	 */
> -	return (dt * 824) / 10;
> -}
> -
> -static int at803x_cdt_start(struct phy_device *phydev,
> -			    u32 cdt_start)
> -{
> -	return phy_write(phydev, AT803X_CDT, cdt_start);
> -}
> -
> -static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
> -					  u32 cdt_en)
> -{
> -	int val, ret;
> -
> -	/* One test run takes about 25ms */
> -	ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
> -				    !(val & cdt_en),
> -				    30000, 100000, true);
> -
> -	return ret < 0 ? ret : 0;
> -}
> -
>  static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
>  {
>  	static const int ethtool_pair[] = {
> diff --git a/drivers/net/phy/qcom/qcom-phy-lib.c b/drivers/net/phy/qcom/qcom-phy-lib.c
> index 7192184429b7..e0295d4b4a51 100644
> --- a/drivers/net/phy/qcom/qcom-phy-lib.c
> +++ b/drivers/net/phy/qcom/qcom-phy-lib.c
> @@ -3,6 +3,9 @@
>  #include <linux/phy.h>
>  #include <linux/module.h>
>  
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +
>  #include "qcom.h"
>  
>  MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
> @@ -51,3 +54,376 @@ int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
>  	return phy_write(phydev, AT803X_DEBUG_DATA, data);
>  }
>  EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
> +
> +int at803x_set_wol(struct phy_device *phydev,
> +		   struct ethtool_wolinfo *wol)
> +{
> +	int ret, irq_enabled;
> +
> +	if (wol->wolopts & WAKE_MAGIC) {
> +		struct net_device *ndev = phydev->attached_dev;
> +		const u8 *mac;
> +		unsigned int i;
> +		static const unsigned int offsets[] = {
> +			AT803X_LOC_MAC_ADDR_32_47_OFFSET,
> +			AT803X_LOC_MAC_ADDR_16_31_OFFSET,
> +			AT803X_LOC_MAC_ADDR_0_15_OFFSET,
> +		};
> +
> +		if (!ndev)
> +			return -ENODEV;
> +
> +		mac = (const u8 *)ndev->dev_addr;
> +
> +		if (!is_valid_ether_addr(mac))
> +			return -EINVAL;
> +
> +		for (i = 0; i < 3; i++)
> +			phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
> +				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
> +
> +		/* Enable WOL interrupt */
> +		ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
> +		if (ret)
> +			return ret;
> +	} else {
> +		/* Disable WOL interrupt */
> +		ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Clear WOL status */
> +	ret = phy_read(phydev, AT803X_INTR_STATUS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Check if there are other interrupts except for WOL triggered when PHY is
> +	 * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
> +	 * be passed up to the interrupt PIN.
> +	 */
> +	irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
> +	if (irq_enabled < 0)
> +		return irq_enabled;
> +
> +	irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
> +	if (ret & irq_enabled && !phy_polling_mode(phydev))
> +		phy_trigger_machine(phydev);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(at803x_set_wol);
> +
> +void at803x_get_wol(struct phy_device *phydev,
> +		    struct ethtool_wolinfo *wol)
> +{
> +	int value;
> +
> +	wol->supported = WAKE_MAGIC;
> +	wol->wolopts = 0;
> +
> +	value = phy_read(phydev, AT803X_INTR_ENABLE);
> +	if (value < 0)
> +		return;
> +
> +	if (value & AT803X_INTR_ENABLE_WOL)
> +		wol->wolopts |= WAKE_MAGIC;
> +}
> +EXPORT_SYMBOL_GPL(at803x_get_wol);
> +
> +int at803x_ack_interrupt(struct phy_device *phydev)
> +{
> +	int err;
> +
> +	err = phy_read(phydev, AT803X_INTR_STATUS);
> +
> +	return (err < 0) ? err : 0;
> +}
> +EXPORT_SYMBOL_GPL(at803x_ack_interrupt);
> +
> +int at803x_config_intr(struct phy_device *phydev)
> +{
> +	int err;
> +	int value;
> +
> +	value = phy_read(phydev, AT803X_INTR_ENABLE);
> +
> +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
> +		/* Clear any pending interrupts */
> +		err = at803x_ack_interrupt(phydev);
> +		if (err)
> +			return err;
> +
> +		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
> +		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
> +		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
> +		value |= AT803X_INTR_ENABLE_LINK_FAIL;
> +		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
> +
> +		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
> +	} else {
> +		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
> +		if (err)
> +			return err;
> +
> +		/* Clear any pending interrupts */
> +		err = at803x_ack_interrupt(phydev);
> +	}
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(at803x_config_intr);
> +
> +irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
> +{
> +	int irq_status, int_enabled;
> +
> +	irq_status = phy_read(phydev, AT803X_INTR_STATUS);
> +	if (irq_status < 0) {
> +		phy_error(phydev);
> +		return IRQ_NONE;
> +	}
> +
> +	/* Read the current enabled interrupts */
> +	int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
> +	if (int_enabled < 0) {
> +		phy_error(phydev);
> +		return IRQ_NONE;
> +	}
> +
> +	/* See if this was one of our enabled interrupts */
> +	if (!(irq_status & int_enabled))
> +		return IRQ_NONE;
> +
> +	phy_trigger_machine(phydev);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL_GPL(at803x_handle_interrupt);
> +
> +int at803x_read_specific_status(struct phy_device *phydev,
> +				struct at803x_ss_mask ss_mask)
> +{
> +	int ss;
> +
> +	/* Read the AT8035 PHY-Specific Status register, which indicates the
> +	 * speed and duplex that the PHY is actually using, irrespective of
> +	 * whether we are in autoneg mode or not.
> +	 */
> +	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
> +	if (ss < 0)
> +		return ss;
> +
> +	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
> +		int sfc, speed;
> +
> +		sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
> +		if (sfc < 0)
> +			return sfc;
> +
> +		speed = ss & ss_mask.speed_mask;
> +		speed >>= ss_mask.speed_shift;
> +
> +		switch (speed) {
> +		case AT803X_SS_SPEED_10:
> +			phydev->speed = SPEED_10;
> +			break;
> +		case AT803X_SS_SPEED_100:
> +			phydev->speed = SPEED_100;
> +			break;
> +		case AT803X_SS_SPEED_1000:
> +			phydev->speed = SPEED_1000;
> +			break;
> +		case QCA808X_SS_SPEED_2500:
> +			phydev->speed = SPEED_2500;
> +			break;
> +		}
> +		if (ss & AT803X_SS_DUPLEX)
> +			phydev->duplex = DUPLEX_FULL;
> +		else
> +			phydev->duplex = DUPLEX_HALF;
> +
> +		if (ss & AT803X_SS_MDIX)
> +			phydev->mdix = ETH_TP_MDI_X;
> +		else
> +			phydev->mdix = ETH_TP_MDI;
> +
> +		switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
> +		case AT803X_SFC_MANUAL_MDI:
> +			phydev->mdix_ctrl = ETH_TP_MDI;
> +			break;
> +		case AT803X_SFC_MANUAL_MDIX:
> +			phydev->mdix_ctrl = ETH_TP_MDI_X;
> +			break;
> +		case AT803X_SFC_AUTOMATIC_CROSSOVER:
> +			phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(at803x_read_specific_status);
> +
> +int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
> +{
> +	u16 val;
> +
> +	switch (ctrl) {
> +	case ETH_TP_MDI:
> +		val = AT803X_SFC_MANUAL_MDI;
> +		break;
> +	case ETH_TP_MDI_X:
> +		val = AT803X_SFC_MANUAL_MDIX;
> +		break;
> +	case ETH_TP_MDI_AUTO:
> +		val = AT803X_SFC_AUTOMATIC_CROSSOVER;
> +		break;
> +	default:
> +		return 0;
> +	}
> +
> +	return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
> +			  AT803X_SFC_MDI_CROSSOVER_MODE_M,
> +			  FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
> +}
> +EXPORT_SYMBOL_GPL(at803x_config_mdix);
> +
> +int at803x_prepare_config_aneg(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Changes of the midx bits are disruptive to the normal operation;
> +	 * therefore any changes to these registers must be followed by a
> +	 * software reset to take effect.
> +	 */
> +	if (ret == 1) {
> +		ret = genphy_soft_reset(phydev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
> +
> +static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
> +{
> +	int val;
> +
> +	val = phy_read(phydev, AT803X_SMART_SPEED);
> +	if (val < 0)
> +		return val;
> +
> +	if (val & AT803X_SMART_SPEED_ENABLE)
> +		*d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
> +	else
> +		*d = DOWNSHIFT_DEV_DISABLE;
> +
> +	return 0;
> +}
> +
> +static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
> +{
> +	u16 mask, set;
> +	int ret;
> +
> +	switch (cnt) {
> +	case DOWNSHIFT_DEV_DEFAULT_COUNT:
> +		cnt = AT803X_DEFAULT_DOWNSHIFT;
> +		fallthrough;
> +	case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
> +		set = AT803X_SMART_SPEED_ENABLE |
> +		      AT803X_SMART_SPEED_BYPASS_TIMER |
> +		      FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
> +		mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
> +		break;
> +	case DOWNSHIFT_DEV_DISABLE:
> +		set = 0;
> +		mask = AT803X_SMART_SPEED_ENABLE |
> +		       AT803X_SMART_SPEED_BYPASS_TIMER;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
> +
> +	/* After changing the smart speed settings, we need to perform a
> +	 * software reset, use phy_init_hw() to make sure we set the
> +	 * reapply any values which might got lost during software reset.
> +	 */
> +	if (ret == 1)
> +		ret = phy_init_hw(phydev);
> +
> +	return ret;
> +}
> +
> +int at803x_get_tunable(struct phy_device *phydev,
> +		       struct ethtool_tunable *tuna, void *data)
> +{
> +	switch (tuna->id) {
> +	case ETHTOOL_PHY_DOWNSHIFT:
> +		return at803x_get_downshift(phydev, data);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(at803x_get_tunable);
> +
> +int at803x_set_tunable(struct phy_device *phydev,
> +		       struct ethtool_tunable *tuna, const void *data)
> +{
> +	switch (tuna->id) {
> +	case ETHTOOL_PHY_DOWNSHIFT:
> +		return at803x_set_downshift(phydev, *(const u8 *)data);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(at803x_set_tunable);
> +
> +int at803x_cdt_fault_length(int dt)
> +{
> +	/* According to the datasheet the distance to the fault is
> +	 * DELTA_TIME * 0.824 meters.
> +	 *
> +	 * The author suspect the correct formula is:
> +	 *
> +	 *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
> +	 *
> +	 * where c is the speed of light, VF is the velocity factor of
> +	 * the twisted pair cable, 125MHz the counter frequency and
> +	 * we need to divide by 2 because the hardware will measure the
> +	 * round trip time to the fault and back to the PHY.
> +	 *
> +	 * With a VF of 0.69 we get the factor 0.824 mentioned in the
> +	 * datasheet.
> +	 */
> +	return (dt * 824) / 10;
> +}
> +EXPORT_SYMBOL_GPL(at803x_cdt_fault_length);
> +
> +int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start)
> +{
> +	return phy_write(phydev, AT803X_CDT, cdt_start);
> +}
> +EXPORT_SYMBOL_GPL(at803x_cdt_start);
> +
> +int at803x_cdt_wait_for_completion(struct phy_device *phydev,
> +				   u32 cdt_en)
> +{
> +	int val, ret;
> +
> +	/* One test run takes about 25ms */
> +	ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
> +				    !(val & cdt_en),
> +				    30000, 100000, true);
> +
> +	return ret < 0 ? ret : 0;
> +}
> +EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
> diff --git a/drivers/net/phy/qcom/qcom.h b/drivers/net/phy/qcom/qcom.h
> index 8eb476d7c282..344f8c01d5b8 100644
> --- a/drivers/net/phy/qcom/qcom.h
> +++ b/drivers/net/phy/qcom/qcom.h
> @@ -1,5 +1,61 @@
>  /* SPDX-License-Identifier: GPL-2.0 */
>  
> +#define AT803X_SPECIFIC_FUNCTION_CONTROL	0x10
> +#define AT803X_SFC_ASSERT_CRS			BIT(11)
> +#define AT803X_SFC_FORCE_LINK			BIT(10)
> +#define AT803X_SFC_MDI_CROSSOVER_MODE_M		GENMASK(6, 5)
> +#define AT803X_SFC_AUTOMATIC_CROSSOVER		0x3
> +#define AT803X_SFC_MANUAL_MDIX			0x1
> +#define AT803X_SFC_MANUAL_MDI			0x0
> +#define AT803X_SFC_SQE_TEST			BIT(2)
> +#define AT803X_SFC_POLARITY_REVERSAL		BIT(1)
> +#define AT803X_SFC_DISABLE_JABBER		BIT(0)
> +
> +#define AT803X_SPECIFIC_STATUS			0x11
> +#define AT803X_SS_SPEED_1000			2
> +#define AT803X_SS_SPEED_100			1
> +#define AT803X_SS_SPEED_10			0
> +#define AT803X_SS_DUPLEX			BIT(13)
> +#define AT803X_SS_SPEED_DUPLEX_RESOLVED		BIT(11)
> +#define AT803X_SS_MDIX				BIT(6)
> +
> +#define QCA808X_SS_SPEED_2500			4
> +
> +#define AT803X_INTR_ENABLE			0x12
> +#define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
> +#define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
> +#define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)
> +#define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)
> +#define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)
> +#define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)
> +#define AT803X_INTR_ENABLE_LINK_FAIL_BX		BIT(8)
> +#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX	BIT(7)
> +#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)
> +#define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)
> +#define AT803X_INTR_ENABLE_WOL			BIT(0)
> +
> +#define AT803X_INTR_STATUS			0x13
> +
> +#define AT803X_SMART_SPEED			0x14
> +#define AT803X_SMART_SPEED_ENABLE		BIT(5)
> +#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK	GENMASK(4, 2)
> +#define AT803X_SMART_SPEED_BYPASS_TIMER		BIT(1)
> +
> +#define AT803X_CDT				0x16
> +#define AT803X_CDT_MDI_PAIR_MASK		GENMASK(9, 8)
> +#define AT803X_CDT_ENABLE_TEST			BIT(0)
> +#define AT803X_CDT_STATUS			0x1c
> +#define AT803X_CDT_STATUS_STAT_NORMAL		0
> +#define AT803X_CDT_STATUS_STAT_SHORT		1
> +#define AT803X_CDT_STATUS_STAT_OPEN		2
> +#define AT803X_CDT_STATUS_STAT_FAIL		3
> +#define AT803X_CDT_STATUS_STAT_MASK		GENMASK(9, 8)
> +#define AT803X_CDT_STATUS_DELTA_TIME_MASK	GENMASK(7, 0)
> +
> +#define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
> +#define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
> +#define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
> +
>  #define AT803X_DEBUG_ADDR			0x1D
>  #define AT803X_DEBUG_DATA			0x1E
>  
> @@ -16,6 +72,10 @@
>  #define   AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE	BIT(13)
>  #define   AT803X_DEBUG_HIB_CTRL_PS_HIB_EN	BIT(15)
>  
> +#define AT803X_DEFAULT_DOWNSHIFT		5
> +#define AT803X_MIN_DOWNSHIFT			2
> +#define AT803X_MAX_DOWNSHIFT			9
> +
>  enum stat_access_type {
>  	PHY,
>  	MMD
> @@ -28,7 +88,31 @@ struct at803x_hw_stat {
>  	enum stat_access_type access_type;
>  };
>  
> +struct at803x_ss_mask {
> +	u16 speed_mask;
> +	u8 speed_shift;
> +};
> +
>  int at803x_debug_reg_read(struct phy_device *phydev, u16 reg);
>  int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
>  			  u16 clear, u16 set);
>  int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data);
> +int at803x_set_wol(struct phy_device *phydev,
> +		   struct ethtool_wolinfo *wol);
> +void at803x_get_wol(struct phy_device *phydev,
> +		    struct ethtool_wolinfo *wol);
> +int at803x_ack_interrupt(struct phy_device *phydev);
> +int at803x_config_intr(struct phy_device *phydev);
> +irqreturn_t at803x_handle_interrupt(struct phy_device *phydev);
> +int at803x_read_specific_status(struct phy_device *phydev,
> +				struct at803x_ss_mask ss_mask);
> +int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
> +int at803x_prepare_config_aneg(struct phy_device *phydev);
> +int at803x_get_tunable(struct phy_device *phydev,
> +		       struct ethtool_tunable *tuna, void *data);
> +int at803x_set_tunable(struct phy_device *phydev,
> +		       struct ethtool_tunable *tuna, const void *data);
> +int at803x_cdt_fault_length(int dt);
> +int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
> +int at803x_cdt_wait_for_completion(struct phy_device *phydev,
> +				   u32 cdt_en);
> -- 
> 2.43.0
> 

-- 
	Ansuel

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ