18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/net/phy/at803x.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Driver for Qualcomm Atheros AR803x PHY
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/phy.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/ethtool_netlink.h>
168c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
178c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
188c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
198c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h>
208c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
218c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
228c2ecf20Sopenharmony_ci#include <dt-bindings/net/qca-ar803x.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define AT803X_SPECIFIC_FUNCTION_CONTROL	0x10
258c2ecf20Sopenharmony_ci#define AT803X_SFC_ASSERT_CRS			BIT(11)
268c2ecf20Sopenharmony_ci#define AT803X_SFC_FORCE_LINK			BIT(10)
278c2ecf20Sopenharmony_ci#define AT803X_SFC_MDI_CROSSOVER_MODE_M		GENMASK(6, 5)
288c2ecf20Sopenharmony_ci#define AT803X_SFC_AUTOMATIC_CROSSOVER		0x3
298c2ecf20Sopenharmony_ci#define AT803X_SFC_MANUAL_MDIX			0x1
308c2ecf20Sopenharmony_ci#define AT803X_SFC_MANUAL_MDI			0x0
318c2ecf20Sopenharmony_ci#define AT803X_SFC_SQE_TEST			BIT(2)
328c2ecf20Sopenharmony_ci#define AT803X_SFC_POLARITY_REVERSAL		BIT(1)
338c2ecf20Sopenharmony_ci#define AT803X_SFC_DISABLE_JABBER		BIT(0)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define AT803X_SPECIFIC_STATUS			0x11
368c2ecf20Sopenharmony_ci#define AT803X_SS_SPEED_MASK			(3 << 14)
378c2ecf20Sopenharmony_ci#define AT803X_SS_SPEED_1000			(2 << 14)
388c2ecf20Sopenharmony_ci#define AT803X_SS_SPEED_100			(1 << 14)
398c2ecf20Sopenharmony_ci#define AT803X_SS_SPEED_10			(0 << 14)
408c2ecf20Sopenharmony_ci#define AT803X_SS_DUPLEX			BIT(13)
418c2ecf20Sopenharmony_ci#define AT803X_SS_SPEED_DUPLEX_RESOLVED		BIT(11)
428c2ecf20Sopenharmony_ci#define AT803X_SS_MDIX				BIT(6)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE			0x12
458c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
468c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
478c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)
488c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)
498c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)
508c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)
518c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)
528c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)
538c2ecf20Sopenharmony_ci#define AT803X_INTR_ENABLE_WOL			BIT(0)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define AT803X_INTR_STATUS			0x13
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define AT803X_SMART_SPEED			0x14
588c2ecf20Sopenharmony_ci#define AT803X_SMART_SPEED_ENABLE		BIT(5)
598c2ecf20Sopenharmony_ci#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK	GENMASK(4, 2)
608c2ecf20Sopenharmony_ci#define AT803X_SMART_SPEED_BYPASS_TIMER		BIT(1)
618c2ecf20Sopenharmony_ci#define AT803X_CDT				0x16
628c2ecf20Sopenharmony_ci#define AT803X_CDT_MDI_PAIR_MASK		GENMASK(9, 8)
638c2ecf20Sopenharmony_ci#define AT803X_CDT_ENABLE_TEST			BIT(0)
648c2ecf20Sopenharmony_ci#define AT803X_CDT_STATUS			0x1c
658c2ecf20Sopenharmony_ci#define AT803X_CDT_STATUS_STAT_NORMAL		0
668c2ecf20Sopenharmony_ci#define AT803X_CDT_STATUS_STAT_SHORT		1
678c2ecf20Sopenharmony_ci#define AT803X_CDT_STATUS_STAT_OPEN		2
688c2ecf20Sopenharmony_ci#define AT803X_CDT_STATUS_STAT_FAIL		3
698c2ecf20Sopenharmony_ci#define AT803X_CDT_STATUS_STAT_MASK		GENMASK(9, 8)
708c2ecf20Sopenharmony_ci#define AT803X_CDT_STATUS_DELTA_TIME_MASK	GENMASK(7, 0)
718c2ecf20Sopenharmony_ci#define AT803X_LED_CONTROL			0x18
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define AT803X_DEVICE_ADDR			0x03
748c2ecf20Sopenharmony_ci#define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
758c2ecf20Sopenharmony_ci#define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
768c2ecf20Sopenharmony_ci#define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
778c2ecf20Sopenharmony_ci#define AT803X_REG_CHIP_CONFIG			0x1f
788c2ecf20Sopenharmony_ci#define AT803X_BT_BX_REG_SEL			0x8000
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define AT803X_DEBUG_ADDR			0x1D
818c2ecf20Sopenharmony_ci#define AT803X_DEBUG_DATA			0x1E
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define AT803X_MODE_CFG_MASK			0x0F
848c2ecf20Sopenharmony_ci#define AT803X_MODE_CFG_SGMII			0x01
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define AT803X_PSSR			0x11	/*PHY-Specific Status Register*/
878c2ecf20Sopenharmony_ci#define AT803X_PSSR_MR_AN_COMPLETE	0x0200
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define AT803X_DEBUG_REG_0			0x00
908c2ecf20Sopenharmony_ci#define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define AT803X_DEBUG_REG_5			0x05
938c2ecf20Sopenharmony_ci#define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define AT803X_DEBUG_REG_1F			0x1F
968c2ecf20Sopenharmony_ci#define AT803X_DEBUG_PLL_ON			BIT(2)
978c2ecf20Sopenharmony_ci#define AT803X_DEBUG_RGMII_1V8			BIT(3)
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/* AT803x supports either the XTAL input pad, an internal PLL or the
1008c2ecf20Sopenharmony_ci * DSP as clock reference for the clock output pad. The XTAL reference
1018c2ecf20Sopenharmony_ci * is only used for 25 MHz output, all other frequencies need the PLL.
1028c2ecf20Sopenharmony_ci * The DSP as a clock reference is used in synchronous ethernet
1038c2ecf20Sopenharmony_ci * applications.
1048c2ecf20Sopenharmony_ci *
1058c2ecf20Sopenharmony_ci * By default the PLL is only enabled if there is a link. Otherwise
1068c2ecf20Sopenharmony_ci * the PHY will go into low power state and disabled the PLL. You can
1078c2ecf20Sopenharmony_ci * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
1088c2ecf20Sopenharmony_ci * enabled.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_ci#define AT803X_MMD7_CLK25M			0x8016
1118c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_MASK			GENMASK(4, 2)
1128c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_25MHZ_XTAL		0
1138c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_25MHZ_DSP		1
1148c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_50MHZ_PLL		2
1158c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_50MHZ_DSP		3
1168c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_62_5MHZ_PLL		4
1178c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_62_5MHZ_DSP		5
1188c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_125MHZ_PLL		6
1198c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_125MHZ_DSP		7
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask
1228c2ecf20Sopenharmony_ci * but doesn't support choosing between XTAL/PLL and DSP.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_ci#define AT8035_CLK_OUT_MASK			GENMASK(4, 3)
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_STRENGTH_MASK		GENMASK(8, 7)
1278c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_STRENGTH_FULL		0
1288c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_STRENGTH_HALF		1
1298c2ecf20Sopenharmony_ci#define AT803X_CLK_OUT_STRENGTH_QUARTER		2
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#define AT803X_DEFAULT_DOWNSHIFT 5
1328c2ecf20Sopenharmony_ci#define AT803X_MIN_DOWNSHIFT 2
1338c2ecf20Sopenharmony_ci#define AT803X_MAX_DOWNSHIFT 9
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define ATH9331_PHY_ID 0x004dd041
1368c2ecf20Sopenharmony_ci#define ATH8030_PHY_ID 0x004dd076
1378c2ecf20Sopenharmony_ci#define ATH8031_PHY_ID 0x004dd074
1388c2ecf20Sopenharmony_ci#define ATH8032_PHY_ID 0x004dd023
1398c2ecf20Sopenharmony_ci#define ATH8035_PHY_ID 0x004dd072
1408c2ecf20Sopenharmony_ci#define AT8030_PHY_ID_MASK			0xffffffef
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
1438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matus Ujhelyi");
1448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistruct at803x_priv {
1478c2ecf20Sopenharmony_ci	int flags;
1488c2ecf20Sopenharmony_ci#define AT803X_KEEP_PLL_ENABLED	BIT(0)	/* don't turn off internal PLL */
1498c2ecf20Sopenharmony_ci	u16 clk_25m_reg;
1508c2ecf20Sopenharmony_ci	u16 clk_25m_mask;
1518c2ecf20Sopenharmony_ci	struct regulator_dev *vddio_rdev;
1528c2ecf20Sopenharmony_ci	struct regulator_dev *vddh_rdev;
1538c2ecf20Sopenharmony_ci	struct regulator *vddio;
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistruct at803x_context {
1578c2ecf20Sopenharmony_ci	u16 bmcr;
1588c2ecf20Sopenharmony_ci	u16 advertise;
1598c2ecf20Sopenharmony_ci	u16 control1000;
1608c2ecf20Sopenharmony_ci	u16 int_enable;
1618c2ecf20Sopenharmony_ci	u16 smart_speed;
1628c2ecf20Sopenharmony_ci	u16 led_control;
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int ret;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
1708c2ecf20Sopenharmony_ci	if (ret < 0)
1718c2ecf20Sopenharmony_ci		return ret;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return phy_read(phydev, AT803X_DEBUG_DATA);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
1778c2ecf20Sopenharmony_ci				 u16 clear, u16 set)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	u16 val;
1808c2ecf20Sopenharmony_ci	int ret;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ret = at803x_debug_reg_read(phydev, reg);
1838c2ecf20Sopenharmony_ci	if (ret < 0)
1848c2ecf20Sopenharmony_ci		return ret;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	val = ret & 0xffff;
1878c2ecf20Sopenharmony_ci	val &= ~clear;
1888c2ecf20Sopenharmony_ci	val |= set;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return phy_write(phydev, AT803X_DEBUG_DATA, val);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int at803x_enable_rx_delay(struct phy_device *phydev)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
1968c2ecf20Sopenharmony_ci				     AT803X_DEBUG_RX_CLK_DLY_EN);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int at803x_enable_tx_delay(struct phy_device *phydev)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0,
2028c2ecf20Sopenharmony_ci				     AT803X_DEBUG_TX_CLK_DLY_EN);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int at803x_disable_rx_delay(struct phy_device *phydev)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
2088c2ecf20Sopenharmony_ci				     AT803X_DEBUG_RX_CLK_DLY_EN, 0);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int at803x_disable_tx_delay(struct phy_device *phydev)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5,
2148c2ecf20Sopenharmony_ci				     AT803X_DEBUG_TX_CLK_DLY_EN, 0);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/* save relevant PHY registers to private copy */
2188c2ecf20Sopenharmony_cistatic void at803x_context_save(struct phy_device *phydev,
2198c2ecf20Sopenharmony_ci				struct at803x_context *context)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	context->bmcr = phy_read(phydev, MII_BMCR);
2228c2ecf20Sopenharmony_ci	context->advertise = phy_read(phydev, MII_ADVERTISE);
2238c2ecf20Sopenharmony_ci	context->control1000 = phy_read(phydev, MII_CTRL1000);
2248c2ecf20Sopenharmony_ci	context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
2258c2ecf20Sopenharmony_ci	context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
2268c2ecf20Sopenharmony_ci	context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/* restore relevant PHY registers from private copy */
2308c2ecf20Sopenharmony_cistatic void at803x_context_restore(struct phy_device *phydev,
2318c2ecf20Sopenharmony_ci				   const struct at803x_context *context)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	phy_write(phydev, MII_BMCR, context->bmcr);
2348c2ecf20Sopenharmony_ci	phy_write(phydev, MII_ADVERTISE, context->advertise);
2358c2ecf20Sopenharmony_ci	phy_write(phydev, MII_CTRL1000, context->control1000);
2368c2ecf20Sopenharmony_ci	phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
2378c2ecf20Sopenharmony_ci	phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
2388c2ecf20Sopenharmony_ci	phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int at803x_set_wol(struct phy_device *phydev,
2428c2ecf20Sopenharmony_ci			  struct ethtool_wolinfo *wol)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct net_device *ndev = phydev->attached_dev;
2458c2ecf20Sopenharmony_ci	const u8 *mac;
2468c2ecf20Sopenharmony_ci	int ret;
2478c2ecf20Sopenharmony_ci	u32 value;
2488c2ecf20Sopenharmony_ci	unsigned int i, offsets[] = {
2498c2ecf20Sopenharmony_ci		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
2508c2ecf20Sopenharmony_ci		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
2518c2ecf20Sopenharmony_ci		AT803X_LOC_MAC_ADDR_0_15_OFFSET,
2528c2ecf20Sopenharmony_ci	};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (!ndev)
2558c2ecf20Sopenharmony_ci		return -ENODEV;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC) {
2588c2ecf20Sopenharmony_ci		mac = (const u8 *) ndev->dev_addr;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		if (!is_valid_ether_addr(mac))
2618c2ecf20Sopenharmony_ci			return -EINVAL;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		for (i = 0; i < 3; i++)
2648c2ecf20Sopenharmony_ci			phy_write_mmd(phydev, AT803X_DEVICE_ADDR, offsets[i],
2658c2ecf20Sopenharmony_ci				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		value = phy_read(phydev, AT803X_INTR_ENABLE);
2688c2ecf20Sopenharmony_ci		value |= AT803X_INTR_ENABLE_WOL;
2698c2ecf20Sopenharmony_ci		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
2708c2ecf20Sopenharmony_ci		if (ret)
2718c2ecf20Sopenharmony_ci			return ret;
2728c2ecf20Sopenharmony_ci		value = phy_read(phydev, AT803X_INTR_STATUS);
2738c2ecf20Sopenharmony_ci	} else {
2748c2ecf20Sopenharmony_ci		value = phy_read(phydev, AT803X_INTR_ENABLE);
2758c2ecf20Sopenharmony_ci		value &= (~AT803X_INTR_ENABLE_WOL);
2768c2ecf20Sopenharmony_ci		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
2778c2ecf20Sopenharmony_ci		if (ret)
2788c2ecf20Sopenharmony_ci			return ret;
2798c2ecf20Sopenharmony_ci		value = phy_read(phydev, AT803X_INTR_STATUS);
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return ret;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void at803x_get_wol(struct phy_device *phydev,
2868c2ecf20Sopenharmony_ci			   struct ethtool_wolinfo *wol)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	u32 value;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	wol->supported = WAKE_MAGIC;
2918c2ecf20Sopenharmony_ci	wol->wolopts = 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	value = phy_read(phydev, AT803X_INTR_ENABLE);
2948c2ecf20Sopenharmony_ci	if (value & AT803X_INTR_ENABLE_WOL)
2958c2ecf20Sopenharmony_ci		wol->wolopts |= WAKE_MAGIC;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int at803x_suspend(struct phy_device *phydev)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	int value;
3018c2ecf20Sopenharmony_ci	int wol_enabled;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	value = phy_read(phydev, AT803X_INTR_ENABLE);
3048c2ecf20Sopenharmony_ci	wol_enabled = value & AT803X_INTR_ENABLE_WOL;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (wol_enabled)
3078c2ecf20Sopenharmony_ci		value = BMCR_ISOLATE;
3088c2ecf20Sopenharmony_ci	else
3098c2ecf20Sopenharmony_ci		value = BMCR_PDOWN;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	phy_modify(phydev, MII_BMCR, 0, value);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic int at803x_resume(struct phy_device *phydev)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
3228c2ecf20Sopenharmony_ci					    unsigned int selector)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct phy_device *phydev = rdev_get_drvdata(rdev);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (selector)
3278c2ecf20Sopenharmony_ci		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
3288c2ecf20Sopenharmony_ci					     0, AT803X_DEBUG_RGMII_1V8);
3298c2ecf20Sopenharmony_ci	else
3308c2ecf20Sopenharmony_ci		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
3318c2ecf20Sopenharmony_ci					     AT803X_DEBUG_RGMII_1V8, 0);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct phy_device *phydev = rdev_get_drvdata(rdev);
3378c2ecf20Sopenharmony_ci	int val;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
3408c2ecf20Sopenharmony_ci	if (val < 0)
3418c2ecf20Sopenharmony_ci		return val;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic const struct regulator_ops vddio_regulator_ops = {
3478c2ecf20Sopenharmony_ci	.list_voltage = regulator_list_voltage_table,
3488c2ecf20Sopenharmony_ci	.set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
3498c2ecf20Sopenharmony_ci	.get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
3508c2ecf20Sopenharmony_ci};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic const unsigned int vddio_voltage_table[] = {
3538c2ecf20Sopenharmony_ci	1500000,
3548c2ecf20Sopenharmony_ci	1800000,
3558c2ecf20Sopenharmony_ci};
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic const struct regulator_desc vddio_desc = {
3588c2ecf20Sopenharmony_ci	.name = "vddio",
3598c2ecf20Sopenharmony_ci	.of_match = of_match_ptr("vddio-regulator"),
3608c2ecf20Sopenharmony_ci	.n_voltages = ARRAY_SIZE(vddio_voltage_table),
3618c2ecf20Sopenharmony_ci	.volt_table = vddio_voltage_table,
3628c2ecf20Sopenharmony_ci	.ops = &vddio_regulator_ops,
3638c2ecf20Sopenharmony_ci	.type = REGULATOR_VOLTAGE,
3648c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3658c2ecf20Sopenharmony_ci};
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic const struct regulator_ops vddh_regulator_ops = {
3688c2ecf20Sopenharmony_ci};
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic const struct regulator_desc vddh_desc = {
3718c2ecf20Sopenharmony_ci	.name = "vddh",
3728c2ecf20Sopenharmony_ci	.of_match = of_match_ptr("vddh-regulator"),
3738c2ecf20Sopenharmony_ci	.n_voltages = 1,
3748c2ecf20Sopenharmony_ci	.fixed_uV = 2500000,
3758c2ecf20Sopenharmony_ci	.ops = &vddh_regulator_ops,
3768c2ecf20Sopenharmony_ci	.type = REGULATOR_VOLTAGE,
3778c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3788c2ecf20Sopenharmony_ci};
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int at8031_register_regulators(struct phy_device *phydev)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct at803x_priv *priv = phydev->priv;
3838c2ecf20Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
3848c2ecf20Sopenharmony_ci	struct regulator_config config = { };
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	config.dev = dev;
3878c2ecf20Sopenharmony_ci	config.driver_data = phydev;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
3908c2ecf20Sopenharmony_ci	if (IS_ERR(priv->vddio_rdev)) {
3918c2ecf20Sopenharmony_ci		phydev_err(phydev, "failed to register VDDIO regulator\n");
3928c2ecf20Sopenharmony_ci		return PTR_ERR(priv->vddio_rdev);
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
3968c2ecf20Sopenharmony_ci	if (IS_ERR(priv->vddh_rdev)) {
3978c2ecf20Sopenharmony_ci		phydev_err(phydev, "failed to register VDDH regulator\n");
3988c2ecf20Sopenharmony_ci		return PTR_ERR(priv->vddh_rdev);
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	return (phydev->phy_id & phydev->drv->phy_id_mask)
4078c2ecf20Sopenharmony_ci		== (phy_id & phydev->drv->phy_id_mask);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int at803x_parse_dt(struct phy_device *phydev)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct device_node *node = phydev->mdio.dev.of_node;
4138c2ecf20Sopenharmony_ci	struct at803x_priv *priv = phydev->priv;
4148c2ecf20Sopenharmony_ci	u32 freq, strength;
4158c2ecf20Sopenharmony_ci	unsigned int sel;
4168c2ecf20Sopenharmony_ci	int ret;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_OF_MDIO))
4198c2ecf20Sopenharmony_ci		return 0;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
4228c2ecf20Sopenharmony_ci	if (!ret) {
4238c2ecf20Sopenharmony_ci		switch (freq) {
4248c2ecf20Sopenharmony_ci		case 25000000:
4258c2ecf20Sopenharmony_ci			sel = AT803X_CLK_OUT_25MHZ_XTAL;
4268c2ecf20Sopenharmony_ci			break;
4278c2ecf20Sopenharmony_ci		case 50000000:
4288c2ecf20Sopenharmony_ci			sel = AT803X_CLK_OUT_50MHZ_PLL;
4298c2ecf20Sopenharmony_ci			break;
4308c2ecf20Sopenharmony_ci		case 62500000:
4318c2ecf20Sopenharmony_ci			sel = AT803X_CLK_OUT_62_5MHZ_PLL;
4328c2ecf20Sopenharmony_ci			break;
4338c2ecf20Sopenharmony_ci		case 125000000:
4348c2ecf20Sopenharmony_ci			sel = AT803X_CLK_OUT_125MHZ_PLL;
4358c2ecf20Sopenharmony_ci			break;
4368c2ecf20Sopenharmony_ci		default:
4378c2ecf20Sopenharmony_ci			phydev_err(phydev, "invalid qca,clk-out-frequency\n");
4388c2ecf20Sopenharmony_ci			return -EINVAL;
4398c2ecf20Sopenharmony_ci		}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
4428c2ecf20Sopenharmony_ci		priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		/* Fixup for the AR8030/AR8035. This chip has another mask and
4458c2ecf20Sopenharmony_ci		 * doesn't support the DSP reference. Eg. the lowest bit of the
4468c2ecf20Sopenharmony_ci		 * mask. The upper two bits select the same frequencies. Mask
4478c2ecf20Sopenharmony_ci		 * the lowest bit here.
4488c2ecf20Sopenharmony_ci		 *
4498c2ecf20Sopenharmony_ci		 * Warning:
4508c2ecf20Sopenharmony_ci		 *   There was no datasheet for the AR8030 available so this is
4518c2ecf20Sopenharmony_ci		 *   just a guess. But the AR8035 is listed as pin compatible
4528c2ecf20Sopenharmony_ci		 *   to the AR8030 so there might be a good chance it works on
4538c2ecf20Sopenharmony_ci		 *   the AR8030 too.
4548c2ecf20Sopenharmony_ci		 */
4558c2ecf20Sopenharmony_ci		if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) ||
4568c2ecf20Sopenharmony_ci		    at803x_match_phy_id(phydev, ATH8035_PHY_ID)) {
4578c2ecf20Sopenharmony_ci			priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
4588c2ecf20Sopenharmony_ci			priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
4598c2ecf20Sopenharmony_ci		}
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
4638c2ecf20Sopenharmony_ci	if (!ret) {
4648c2ecf20Sopenharmony_ci		priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK;
4658c2ecf20Sopenharmony_ci		switch (strength) {
4668c2ecf20Sopenharmony_ci		case AR803X_STRENGTH_FULL:
4678c2ecf20Sopenharmony_ci			priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL;
4688c2ecf20Sopenharmony_ci			break;
4698c2ecf20Sopenharmony_ci		case AR803X_STRENGTH_HALF:
4708c2ecf20Sopenharmony_ci			priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF;
4718c2ecf20Sopenharmony_ci			break;
4728c2ecf20Sopenharmony_ci		case AR803X_STRENGTH_QUARTER:
4738c2ecf20Sopenharmony_ci			priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER;
4748c2ecf20Sopenharmony_ci			break;
4758c2ecf20Sopenharmony_ci		default:
4768c2ecf20Sopenharmony_ci			phydev_err(phydev, "invalid qca,clk-out-strength\n");
4778c2ecf20Sopenharmony_ci			return -EINVAL;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
4828c2ecf20Sopenharmony_ci	 * options.
4838c2ecf20Sopenharmony_ci	 */
4848c2ecf20Sopenharmony_ci	if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
4858c2ecf20Sopenharmony_ci		if (of_property_read_bool(node, "qca,keep-pll-enabled"))
4868c2ecf20Sopenharmony_ci			priv->flags |= AT803X_KEEP_PLL_ENABLED;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		ret = at8031_register_regulators(phydev);
4898c2ecf20Sopenharmony_ci		if (ret < 0)
4908c2ecf20Sopenharmony_ci			return ret;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		priv->vddio = devm_regulator_get_optional(&phydev->mdio.dev,
4938c2ecf20Sopenharmony_ci							  "vddio");
4948c2ecf20Sopenharmony_ci		if (IS_ERR(priv->vddio)) {
4958c2ecf20Sopenharmony_ci			phydev_err(phydev, "failed to get VDDIO regulator\n");
4968c2ecf20Sopenharmony_ci			return PTR_ERR(priv->vddio);
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		ret = regulator_enable(priv->vddio);
5008c2ecf20Sopenharmony_ci		if (ret < 0)
5018c2ecf20Sopenharmony_ci			return ret;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return 0;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic int at803x_probe(struct phy_device *phydev)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
5108c2ecf20Sopenharmony_ci	struct at803x_priv *priv;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
5138c2ecf20Sopenharmony_ci	if (!priv)
5148c2ecf20Sopenharmony_ci		return -ENOMEM;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	phydev->priv = priv;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return at803x_parse_dt(phydev);
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic void at803x_remove(struct phy_device *phydev)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct at803x_priv *priv = phydev->priv;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (priv->vddio)
5268c2ecf20Sopenharmony_ci		regulator_disable(priv->vddio);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic int at803x_clk_out_config(struct phy_device *phydev)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct at803x_priv *priv = phydev->priv;
5328c2ecf20Sopenharmony_ci	int val;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (!priv->clk_25m_mask)
5358c2ecf20Sopenharmony_ci		return 0;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M);
5388c2ecf20Sopenharmony_ci	if (val < 0)
5398c2ecf20Sopenharmony_ci		return val;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	val &= ~priv->clk_25m_mask;
5428c2ecf20Sopenharmony_ci	val |= priv->clk_25m_reg;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val);
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int at8031_pll_config(struct phy_device *phydev)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct at803x_priv *priv = phydev->priv;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/* The default after hardware reset is PLL OFF. After a soft reset, the
5528c2ecf20Sopenharmony_ci	 * values are retained.
5538c2ecf20Sopenharmony_ci	 */
5548c2ecf20Sopenharmony_ci	if (priv->flags & AT803X_KEEP_PLL_ENABLED)
5558c2ecf20Sopenharmony_ci		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
5568c2ecf20Sopenharmony_ci					     0, AT803X_DEBUG_PLL_ON);
5578c2ecf20Sopenharmony_ci	else
5588c2ecf20Sopenharmony_ci		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
5598c2ecf20Sopenharmony_ci					     AT803X_DEBUG_PLL_ON, 0);
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic int at803x_config_init(struct phy_device *phydev)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	int ret;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* The RX and TX delay default is:
5678c2ecf20Sopenharmony_ci	 *   after HW reset: RX delay enabled and TX delay disabled
5688c2ecf20Sopenharmony_ci	 *   after SW reset: RX delay enabled, while TX delay retains the
5698c2ecf20Sopenharmony_ci	 *   value before reset.
5708c2ecf20Sopenharmony_ci	 */
5718c2ecf20Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
5728c2ecf20Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
5738c2ecf20Sopenharmony_ci		ret = at803x_enable_rx_delay(phydev);
5748c2ecf20Sopenharmony_ci	else
5758c2ecf20Sopenharmony_ci		ret = at803x_disable_rx_delay(phydev);
5768c2ecf20Sopenharmony_ci	if (ret < 0)
5778c2ecf20Sopenharmony_ci		return ret;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
5808c2ecf20Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
5818c2ecf20Sopenharmony_ci		ret = at803x_enable_tx_delay(phydev);
5828c2ecf20Sopenharmony_ci	else
5838c2ecf20Sopenharmony_ci		ret = at803x_disable_tx_delay(phydev);
5848c2ecf20Sopenharmony_ci	if (ret < 0)
5858c2ecf20Sopenharmony_ci		return ret;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	ret = at803x_clk_out_config(phydev);
5888c2ecf20Sopenharmony_ci	if (ret < 0)
5898c2ecf20Sopenharmony_ci		return ret;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
5928c2ecf20Sopenharmony_ci		ret = at8031_pll_config(phydev);
5938c2ecf20Sopenharmony_ci		if (ret < 0)
5948c2ecf20Sopenharmony_ci			return ret;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return 0;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic int at803x_ack_interrupt(struct phy_device *phydev)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	int err;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	err = phy_read(phydev, AT803X_INTR_STATUS);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return (err < 0) ? err : 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int at803x_config_intr(struct phy_device *phydev)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	int err;
6128c2ecf20Sopenharmony_ci	int value;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	value = phy_read(phydev, AT803X_INTR_ENABLE);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
6178c2ecf20Sopenharmony_ci		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
6188c2ecf20Sopenharmony_ci		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
6198c2ecf20Sopenharmony_ci		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
6208c2ecf20Sopenharmony_ci		value |= AT803X_INTR_ENABLE_LINK_FAIL;
6218c2ecf20Sopenharmony_ci		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci	else
6268c2ecf20Sopenharmony_ci		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	return err;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic void at803x_link_change_notify(struct phy_device *phydev)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	/*
6348c2ecf20Sopenharmony_ci	 * Conduct a hardware reset for AT8030 every time a link loss is
6358c2ecf20Sopenharmony_ci	 * signalled. This is necessary to circumvent a hardware bug that
6368c2ecf20Sopenharmony_ci	 * occurs when the cable is unplugged while TX packets are pending
6378c2ecf20Sopenharmony_ci	 * in the FIFO. In such cases, the FIFO enters an error mode it
6388c2ecf20Sopenharmony_ci	 * cannot recover from by software.
6398c2ecf20Sopenharmony_ci	 */
6408c2ecf20Sopenharmony_ci	if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
6418c2ecf20Sopenharmony_ci		struct at803x_context context;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		at803x_context_save(phydev, &context);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		phy_device_reset(phydev, 1);
6468c2ecf20Sopenharmony_ci		msleep(1);
6478c2ecf20Sopenharmony_ci		phy_device_reset(phydev, 0);
6488c2ecf20Sopenharmony_ci		msleep(1);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		at803x_context_restore(phydev, &context);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci		phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic int at803x_aneg_done(struct phy_device *phydev)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	int ccr;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	int aneg_done = genphy_aneg_done(phydev);
6618c2ecf20Sopenharmony_ci	if (aneg_done != BMSR_ANEGCOMPLETE)
6628c2ecf20Sopenharmony_ci		return aneg_done;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	/*
6658c2ecf20Sopenharmony_ci	 * in SGMII mode, if copper side autoneg is successful,
6668c2ecf20Sopenharmony_ci	 * also check SGMII side autoneg result
6678c2ecf20Sopenharmony_ci	 */
6688c2ecf20Sopenharmony_ci	ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
6698c2ecf20Sopenharmony_ci	if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII)
6708c2ecf20Sopenharmony_ci		return aneg_done;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* switch to SGMII/fiber page */
6738c2ecf20Sopenharmony_ci	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	/* check if the SGMII link is OK. */
6768c2ecf20Sopenharmony_ci	if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) {
6778c2ecf20Sopenharmony_ci		phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n");
6788c2ecf20Sopenharmony_ci		aneg_done = 0;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci	/* switch back to copper page */
6818c2ecf20Sopenharmony_ci	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	return aneg_done;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic int at803x_read_status(struct phy_device *phydev)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	int ss, err, old_link = phydev->link;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/* Update the link, but return if there was an error */
6918c2ecf20Sopenharmony_ci	err = genphy_update_link(phydev);
6928c2ecf20Sopenharmony_ci	if (err)
6938c2ecf20Sopenharmony_ci		return err;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* why bother the PHY if nothing can have changed */
6968c2ecf20Sopenharmony_ci	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
6978c2ecf20Sopenharmony_ci		return 0;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	phydev->speed = SPEED_UNKNOWN;
7008c2ecf20Sopenharmony_ci	phydev->duplex = DUPLEX_UNKNOWN;
7018c2ecf20Sopenharmony_ci	phydev->pause = 0;
7028c2ecf20Sopenharmony_ci	phydev->asym_pause = 0;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	err = genphy_read_lpa(phydev);
7058c2ecf20Sopenharmony_ci	if (err < 0)
7068c2ecf20Sopenharmony_ci		return err;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* Read the AT8035 PHY-Specific Status register, which indicates the
7098c2ecf20Sopenharmony_ci	 * speed and duplex that the PHY is actually using, irrespective of
7108c2ecf20Sopenharmony_ci	 * whether we are in autoneg mode or not.
7118c2ecf20Sopenharmony_ci	 */
7128c2ecf20Sopenharmony_ci	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
7138c2ecf20Sopenharmony_ci	if (ss < 0)
7148c2ecf20Sopenharmony_ci		return ss;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
7178c2ecf20Sopenharmony_ci		int sfc;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci		sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
7208c2ecf20Sopenharmony_ci		if (sfc < 0)
7218c2ecf20Sopenharmony_ci			return sfc;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		switch (ss & AT803X_SS_SPEED_MASK) {
7248c2ecf20Sopenharmony_ci		case AT803X_SS_SPEED_10:
7258c2ecf20Sopenharmony_ci			phydev->speed = SPEED_10;
7268c2ecf20Sopenharmony_ci			break;
7278c2ecf20Sopenharmony_ci		case AT803X_SS_SPEED_100:
7288c2ecf20Sopenharmony_ci			phydev->speed = SPEED_100;
7298c2ecf20Sopenharmony_ci			break;
7308c2ecf20Sopenharmony_ci		case AT803X_SS_SPEED_1000:
7318c2ecf20Sopenharmony_ci			phydev->speed = SPEED_1000;
7328c2ecf20Sopenharmony_ci			break;
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci		if (ss & AT803X_SS_DUPLEX)
7358c2ecf20Sopenharmony_ci			phydev->duplex = DUPLEX_FULL;
7368c2ecf20Sopenharmony_ci		else
7378c2ecf20Sopenharmony_ci			phydev->duplex = DUPLEX_HALF;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		if (ss & AT803X_SS_MDIX)
7408c2ecf20Sopenharmony_ci			phydev->mdix = ETH_TP_MDI_X;
7418c2ecf20Sopenharmony_ci		else
7428c2ecf20Sopenharmony_ci			phydev->mdix = ETH_TP_MDI;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
7458c2ecf20Sopenharmony_ci		case AT803X_SFC_MANUAL_MDI:
7468c2ecf20Sopenharmony_ci			phydev->mdix_ctrl = ETH_TP_MDI;
7478c2ecf20Sopenharmony_ci			break;
7488c2ecf20Sopenharmony_ci		case AT803X_SFC_MANUAL_MDIX:
7498c2ecf20Sopenharmony_ci			phydev->mdix_ctrl = ETH_TP_MDI_X;
7508c2ecf20Sopenharmony_ci			break;
7518c2ecf20Sopenharmony_ci		case AT803X_SFC_AUTOMATIC_CROSSOVER:
7528c2ecf20Sopenharmony_ci			phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
7538c2ecf20Sopenharmony_ci			break;
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
7588c2ecf20Sopenharmony_ci		phy_resolve_aneg_pause(phydev);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return 0;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	u16 val;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	switch (ctrl) {
7688c2ecf20Sopenharmony_ci	case ETH_TP_MDI:
7698c2ecf20Sopenharmony_ci		val = AT803X_SFC_MANUAL_MDI;
7708c2ecf20Sopenharmony_ci		break;
7718c2ecf20Sopenharmony_ci	case ETH_TP_MDI_X:
7728c2ecf20Sopenharmony_ci		val = AT803X_SFC_MANUAL_MDIX;
7738c2ecf20Sopenharmony_ci		break;
7748c2ecf20Sopenharmony_ci	case ETH_TP_MDI_AUTO:
7758c2ecf20Sopenharmony_ci		val = AT803X_SFC_AUTOMATIC_CROSSOVER;
7768c2ecf20Sopenharmony_ci		break;
7778c2ecf20Sopenharmony_ci	default:
7788c2ecf20Sopenharmony_ci		return 0;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
7828c2ecf20Sopenharmony_ci			  AT803X_SFC_MDI_CROSSOVER_MODE_M,
7838c2ecf20Sopenharmony_ci			  FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic int at803x_config_aneg(struct phy_device *phydev)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	int ret;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
7918c2ecf20Sopenharmony_ci	if (ret < 0)
7928c2ecf20Sopenharmony_ci		return ret;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/* Changes of the midx bits are disruptive to the normal operation;
7958c2ecf20Sopenharmony_ci	 * therefore any changes to these registers must be followed by a
7968c2ecf20Sopenharmony_ci	 * software reset to take effect.
7978c2ecf20Sopenharmony_ci	 */
7988c2ecf20Sopenharmony_ci	if (ret == 1) {
7998c2ecf20Sopenharmony_ci		ret = genphy_soft_reset(phydev);
8008c2ecf20Sopenharmony_ci		if (ret < 0)
8018c2ecf20Sopenharmony_ci			return ret;
8028c2ecf20Sopenharmony_ci	}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	return genphy_config_aneg(phydev);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic int at803x_get_downshift(struct phy_device *phydev, u8 *d)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	int val;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	val = phy_read(phydev, AT803X_SMART_SPEED);
8128c2ecf20Sopenharmony_ci	if (val < 0)
8138c2ecf20Sopenharmony_ci		return val;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	if (val & AT803X_SMART_SPEED_ENABLE)
8168c2ecf20Sopenharmony_ci		*d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
8178c2ecf20Sopenharmony_ci	else
8188c2ecf20Sopenharmony_ci		*d = DOWNSHIFT_DEV_DISABLE;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	return 0;
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	u16 mask, set;
8268c2ecf20Sopenharmony_ci	int ret;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	switch (cnt) {
8298c2ecf20Sopenharmony_ci	case DOWNSHIFT_DEV_DEFAULT_COUNT:
8308c2ecf20Sopenharmony_ci		cnt = AT803X_DEFAULT_DOWNSHIFT;
8318c2ecf20Sopenharmony_ci		fallthrough;
8328c2ecf20Sopenharmony_ci	case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
8338c2ecf20Sopenharmony_ci		set = AT803X_SMART_SPEED_ENABLE |
8348c2ecf20Sopenharmony_ci		      AT803X_SMART_SPEED_BYPASS_TIMER |
8358c2ecf20Sopenharmony_ci		      FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
8368c2ecf20Sopenharmony_ci		mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
8378c2ecf20Sopenharmony_ci		break;
8388c2ecf20Sopenharmony_ci	case DOWNSHIFT_DEV_DISABLE:
8398c2ecf20Sopenharmony_ci		set = 0;
8408c2ecf20Sopenharmony_ci		mask = AT803X_SMART_SPEED_ENABLE |
8418c2ecf20Sopenharmony_ci		       AT803X_SMART_SPEED_BYPASS_TIMER;
8428c2ecf20Sopenharmony_ci		break;
8438c2ecf20Sopenharmony_ci	default:
8448c2ecf20Sopenharmony_ci		return -EINVAL;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	/* After changing the smart speed settings, we need to perform a
8508c2ecf20Sopenharmony_ci	 * software reset, use phy_init_hw() to make sure we set the
8518c2ecf20Sopenharmony_ci	 * reapply any values which might got lost during software reset.
8528c2ecf20Sopenharmony_ci	 */
8538c2ecf20Sopenharmony_ci	if (ret == 1)
8548c2ecf20Sopenharmony_ci		ret = phy_init_hw(phydev);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	return ret;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int at803x_get_tunable(struct phy_device *phydev,
8608c2ecf20Sopenharmony_ci			      struct ethtool_tunable *tuna, void *data)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	switch (tuna->id) {
8638c2ecf20Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
8648c2ecf20Sopenharmony_ci		return at803x_get_downshift(phydev, data);
8658c2ecf20Sopenharmony_ci	default:
8668c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_cistatic int at803x_set_tunable(struct phy_device *phydev,
8718c2ecf20Sopenharmony_ci			      struct ethtool_tunable *tuna, const void *data)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	switch (tuna->id) {
8748c2ecf20Sopenharmony_ci	case ETHTOOL_PHY_DOWNSHIFT:
8758c2ecf20Sopenharmony_ci		return at803x_set_downshift(phydev, *(const u8 *)data);
8768c2ecf20Sopenharmony_ci	default:
8778c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int at803x_cable_test_result_trans(u16 status)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
8848c2ecf20Sopenharmony_ci	case AT803X_CDT_STATUS_STAT_NORMAL:
8858c2ecf20Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
8868c2ecf20Sopenharmony_ci	case AT803X_CDT_STATUS_STAT_SHORT:
8878c2ecf20Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
8888c2ecf20Sopenharmony_ci	case AT803X_CDT_STATUS_STAT_OPEN:
8898c2ecf20Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
8908c2ecf20Sopenharmony_ci	case AT803X_CDT_STATUS_STAT_FAIL:
8918c2ecf20Sopenharmony_ci	default:
8928c2ecf20Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic bool at803x_cdt_test_failed(u16 status)
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) ==
8998c2ecf20Sopenharmony_ci		AT803X_CDT_STATUS_STAT_FAIL;
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_cistatic bool at803x_cdt_fault_length_valid(u16 status)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
9058c2ecf20Sopenharmony_ci	case AT803X_CDT_STATUS_STAT_OPEN:
9068c2ecf20Sopenharmony_ci	case AT803X_CDT_STATUS_STAT_SHORT:
9078c2ecf20Sopenharmony_ci		return true;
9088c2ecf20Sopenharmony_ci	}
9098c2ecf20Sopenharmony_ci	return false;
9108c2ecf20Sopenharmony_ci}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_cistatic int at803x_cdt_fault_length(u16 status)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	int dt;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/* According to the datasheet the distance to the fault is
9178c2ecf20Sopenharmony_ci	 * DELTA_TIME * 0.824 meters.
9188c2ecf20Sopenharmony_ci	 *
9198c2ecf20Sopenharmony_ci	 * The author suspect the correct formula is:
9208c2ecf20Sopenharmony_ci	 *
9218c2ecf20Sopenharmony_ci	 *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
9228c2ecf20Sopenharmony_ci	 *
9238c2ecf20Sopenharmony_ci	 * where c is the speed of light, VF is the velocity factor of
9248c2ecf20Sopenharmony_ci	 * the twisted pair cable, 125MHz the counter frequency and
9258c2ecf20Sopenharmony_ci	 * we need to divide by 2 because the hardware will measure the
9268c2ecf20Sopenharmony_ci	 * round trip time to the fault and back to the PHY.
9278c2ecf20Sopenharmony_ci	 *
9288c2ecf20Sopenharmony_ci	 * With a VF of 0.69 we get the factor 0.824 mentioned in the
9298c2ecf20Sopenharmony_ci	 * datasheet.
9308c2ecf20Sopenharmony_ci	 */
9318c2ecf20Sopenharmony_ci	dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	return (dt * 824) / 10;
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_cistatic int at803x_cdt_start(struct phy_device *phydev, int pair)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci	u16 cdt;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
9418c2ecf20Sopenharmony_ci	      AT803X_CDT_ENABLE_TEST;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	return phy_write(phydev, AT803X_CDT, cdt);
9448c2ecf20Sopenharmony_ci}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_cistatic int at803x_cdt_wait_for_completion(struct phy_device *phydev)
9478c2ecf20Sopenharmony_ci{
9488c2ecf20Sopenharmony_ci	int val, ret;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/* One test run takes about 25ms */
9518c2ecf20Sopenharmony_ci	ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
9528c2ecf20Sopenharmony_ci				    !(val & AT803X_CDT_ENABLE_TEST),
9538c2ecf20Sopenharmony_ci				    30000, 100000, true);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	return ret < 0 ? ret : 0;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	static const int ethtool_pair[] = {
9618c2ecf20Sopenharmony_ci		ETHTOOL_A_CABLE_PAIR_A,
9628c2ecf20Sopenharmony_ci		ETHTOOL_A_CABLE_PAIR_B,
9638c2ecf20Sopenharmony_ci		ETHTOOL_A_CABLE_PAIR_C,
9648c2ecf20Sopenharmony_ci		ETHTOOL_A_CABLE_PAIR_D,
9658c2ecf20Sopenharmony_ci	};
9668c2ecf20Sopenharmony_ci	int ret, val;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	ret = at803x_cdt_start(phydev, pair);
9698c2ecf20Sopenharmony_ci	if (ret)
9708c2ecf20Sopenharmony_ci		return ret;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	ret = at803x_cdt_wait_for_completion(phydev);
9738c2ecf20Sopenharmony_ci	if (ret)
9748c2ecf20Sopenharmony_ci		return ret;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	val = phy_read(phydev, AT803X_CDT_STATUS);
9778c2ecf20Sopenharmony_ci	if (val < 0)
9788c2ecf20Sopenharmony_ci		return val;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (at803x_cdt_test_failed(val))
9818c2ecf20Sopenharmony_ci		return 0;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	ethnl_cable_test_result(phydev, ethtool_pair[pair],
9848c2ecf20Sopenharmony_ci				at803x_cable_test_result_trans(val));
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	if (at803x_cdt_fault_length_valid(val))
9878c2ecf20Sopenharmony_ci		ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
9888c2ecf20Sopenharmony_ci					      at803x_cdt_fault_length(val));
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	return 1;
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_cistatic int at803x_cable_test_get_status(struct phy_device *phydev,
9948c2ecf20Sopenharmony_ci					bool *finished)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	unsigned long pair_mask;
9978c2ecf20Sopenharmony_ci	int retries = 20;
9988c2ecf20Sopenharmony_ci	int pair, ret;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	if (phydev->phy_id == ATH9331_PHY_ID ||
10018c2ecf20Sopenharmony_ci	    phydev->phy_id == ATH8032_PHY_ID)
10028c2ecf20Sopenharmony_ci		pair_mask = 0x3;
10038c2ecf20Sopenharmony_ci	else
10048c2ecf20Sopenharmony_ci		pair_mask = 0xf;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	*finished = false;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	/* According to the datasheet the CDT can be performed when
10098c2ecf20Sopenharmony_ci	 * there is no link partner or when the link partner is
10108c2ecf20Sopenharmony_ci	 * auto-negotiating. Starting the test will restart the AN
10118c2ecf20Sopenharmony_ci	 * automatically. It seems that doing this repeatedly we will
10128c2ecf20Sopenharmony_ci	 * get a slot where our link partner won't disturb our
10138c2ecf20Sopenharmony_ci	 * measurement.
10148c2ecf20Sopenharmony_ci	 */
10158c2ecf20Sopenharmony_ci	while (pair_mask && retries--) {
10168c2ecf20Sopenharmony_ci		for_each_set_bit(pair, &pair_mask, 4) {
10178c2ecf20Sopenharmony_ci			ret = at803x_cable_test_one_pair(phydev, pair);
10188c2ecf20Sopenharmony_ci			if (ret < 0)
10198c2ecf20Sopenharmony_ci				return ret;
10208c2ecf20Sopenharmony_ci			if (ret)
10218c2ecf20Sopenharmony_ci				clear_bit(pair, &pair_mask);
10228c2ecf20Sopenharmony_ci		}
10238c2ecf20Sopenharmony_ci		if (pair_mask)
10248c2ecf20Sopenharmony_ci			msleep(250);
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	*finished = true;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	return 0;
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic int at803x_cable_test_start(struct phy_device *phydev)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	/* Enable auto-negotiation, but advertise no capabilities, no link
10358c2ecf20Sopenharmony_ci	 * will be established. A restart of the auto-negotiation is not
10368c2ecf20Sopenharmony_ci	 * required, because the cable test will automatically break the link.
10378c2ecf20Sopenharmony_ci	 */
10388c2ecf20Sopenharmony_ci	phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
10398c2ecf20Sopenharmony_ci	phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
10408c2ecf20Sopenharmony_ci	if (phydev->phy_id != ATH9331_PHY_ID &&
10418c2ecf20Sopenharmony_ci	    phydev->phy_id != ATH8032_PHY_ID)
10428c2ecf20Sopenharmony_ci		phy_write(phydev, MII_CTRL1000, 0);
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	/* we do all the (time consuming) work later */
10458c2ecf20Sopenharmony_ci	return 0;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic struct phy_driver at803x_driver[] = {
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	/* Qualcomm Atheros AR8035 */
10518c2ecf20Sopenharmony_ci	PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
10528c2ecf20Sopenharmony_ci	.name			= "Qualcomm Atheros AR8035",
10538c2ecf20Sopenharmony_ci	.flags			= PHY_POLL_CABLE_TEST,
10548c2ecf20Sopenharmony_ci	.probe			= at803x_probe,
10558c2ecf20Sopenharmony_ci	.remove			= at803x_remove,
10568c2ecf20Sopenharmony_ci	.config_aneg		= at803x_config_aneg,
10578c2ecf20Sopenharmony_ci	.config_init		= at803x_config_init,
10588c2ecf20Sopenharmony_ci	.soft_reset		= genphy_soft_reset,
10598c2ecf20Sopenharmony_ci	.set_wol		= at803x_set_wol,
10608c2ecf20Sopenharmony_ci	.get_wol		= at803x_get_wol,
10618c2ecf20Sopenharmony_ci	.suspend		= at803x_suspend,
10628c2ecf20Sopenharmony_ci	.resume			= at803x_resume,
10638c2ecf20Sopenharmony_ci	/* PHY_GBIT_FEATURES */
10648c2ecf20Sopenharmony_ci	.read_status		= at803x_read_status,
10658c2ecf20Sopenharmony_ci	.ack_interrupt		= at803x_ack_interrupt,
10668c2ecf20Sopenharmony_ci	.config_intr		= at803x_config_intr,
10678c2ecf20Sopenharmony_ci	.get_tunable		= at803x_get_tunable,
10688c2ecf20Sopenharmony_ci	.set_tunable		= at803x_set_tunable,
10698c2ecf20Sopenharmony_ci	.cable_test_start	= at803x_cable_test_start,
10708c2ecf20Sopenharmony_ci	.cable_test_get_status	= at803x_cable_test_get_status,
10718c2ecf20Sopenharmony_ci}, {
10728c2ecf20Sopenharmony_ci	/* Qualcomm Atheros AR8030 */
10738c2ecf20Sopenharmony_ci	.phy_id			= ATH8030_PHY_ID,
10748c2ecf20Sopenharmony_ci	.name			= "Qualcomm Atheros AR8030",
10758c2ecf20Sopenharmony_ci	.phy_id_mask		= AT8030_PHY_ID_MASK,
10768c2ecf20Sopenharmony_ci	.probe			= at803x_probe,
10778c2ecf20Sopenharmony_ci	.remove			= at803x_remove,
10788c2ecf20Sopenharmony_ci	.config_init		= at803x_config_init,
10798c2ecf20Sopenharmony_ci	.link_change_notify	= at803x_link_change_notify,
10808c2ecf20Sopenharmony_ci	.set_wol		= at803x_set_wol,
10818c2ecf20Sopenharmony_ci	.get_wol		= at803x_get_wol,
10828c2ecf20Sopenharmony_ci	.suspend		= at803x_suspend,
10838c2ecf20Sopenharmony_ci	.resume			= at803x_resume,
10848c2ecf20Sopenharmony_ci	/* PHY_BASIC_FEATURES */
10858c2ecf20Sopenharmony_ci	.ack_interrupt		= at803x_ack_interrupt,
10868c2ecf20Sopenharmony_ci	.config_intr		= at803x_config_intr,
10878c2ecf20Sopenharmony_ci}, {
10888c2ecf20Sopenharmony_ci	/* Qualcomm Atheros AR8031/AR8033 */
10898c2ecf20Sopenharmony_ci	PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
10908c2ecf20Sopenharmony_ci	.name			= "Qualcomm Atheros AR8031/AR8033",
10918c2ecf20Sopenharmony_ci	.flags			= PHY_POLL_CABLE_TEST,
10928c2ecf20Sopenharmony_ci	.probe			= at803x_probe,
10938c2ecf20Sopenharmony_ci	.remove			= at803x_remove,
10948c2ecf20Sopenharmony_ci	.config_init		= at803x_config_init,
10958c2ecf20Sopenharmony_ci	.soft_reset		= genphy_soft_reset,
10968c2ecf20Sopenharmony_ci	.set_wol		= at803x_set_wol,
10978c2ecf20Sopenharmony_ci	.get_wol		= at803x_get_wol,
10988c2ecf20Sopenharmony_ci	.suspend		= at803x_suspend,
10998c2ecf20Sopenharmony_ci	.resume			= at803x_resume,
11008c2ecf20Sopenharmony_ci	/* PHY_GBIT_FEATURES */
11018c2ecf20Sopenharmony_ci	.read_status		= at803x_read_status,
11028c2ecf20Sopenharmony_ci	.aneg_done		= at803x_aneg_done,
11038c2ecf20Sopenharmony_ci	.ack_interrupt		= &at803x_ack_interrupt,
11048c2ecf20Sopenharmony_ci	.config_intr		= &at803x_config_intr,
11058c2ecf20Sopenharmony_ci	.get_tunable		= at803x_get_tunable,
11068c2ecf20Sopenharmony_ci	.set_tunable		= at803x_set_tunable,
11078c2ecf20Sopenharmony_ci	.cable_test_start	= at803x_cable_test_start,
11088c2ecf20Sopenharmony_ci	.cable_test_get_status	= at803x_cable_test_get_status,
11098c2ecf20Sopenharmony_ci}, {
11108c2ecf20Sopenharmony_ci	/* Qualcomm Atheros AR8032 */
11118c2ecf20Sopenharmony_ci	PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
11128c2ecf20Sopenharmony_ci	.name			= "Qualcomm Atheros AR8032",
11138c2ecf20Sopenharmony_ci	.probe			= at803x_probe,
11148c2ecf20Sopenharmony_ci	.remove			= at803x_remove,
11158c2ecf20Sopenharmony_ci	.flags			= PHY_POLL_CABLE_TEST,
11168c2ecf20Sopenharmony_ci	.config_init		= at803x_config_init,
11178c2ecf20Sopenharmony_ci	.link_change_notify	= at803x_link_change_notify,
11188c2ecf20Sopenharmony_ci	.suspend		= at803x_suspend,
11198c2ecf20Sopenharmony_ci	.resume			= at803x_resume,
11208c2ecf20Sopenharmony_ci	/* PHY_BASIC_FEATURES */
11218c2ecf20Sopenharmony_ci	.ack_interrupt		= at803x_ack_interrupt,
11228c2ecf20Sopenharmony_ci	.config_intr		= at803x_config_intr,
11238c2ecf20Sopenharmony_ci	.cable_test_start	= at803x_cable_test_start,
11248c2ecf20Sopenharmony_ci	.cable_test_get_status	= at803x_cable_test_get_status,
11258c2ecf20Sopenharmony_ci}, {
11268c2ecf20Sopenharmony_ci	/* ATHEROS AR9331 */
11278c2ecf20Sopenharmony_ci	PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
11288c2ecf20Sopenharmony_ci	.name			= "Qualcomm Atheros AR9331 built-in PHY",
11298c2ecf20Sopenharmony_ci	.suspend		= at803x_suspend,
11308c2ecf20Sopenharmony_ci	.resume			= at803x_resume,
11318c2ecf20Sopenharmony_ci	.flags			= PHY_POLL_CABLE_TEST,
11328c2ecf20Sopenharmony_ci	/* PHY_BASIC_FEATURES */
11338c2ecf20Sopenharmony_ci	.ack_interrupt		= &at803x_ack_interrupt,
11348c2ecf20Sopenharmony_ci	.config_intr		= &at803x_config_intr,
11358c2ecf20Sopenharmony_ci	.cable_test_start	= at803x_cable_test_start,
11368c2ecf20Sopenharmony_ci	.cable_test_get_status	= at803x_cable_test_get_status,
11378c2ecf20Sopenharmony_ci	.read_status		= at803x_read_status,
11388c2ecf20Sopenharmony_ci	.soft_reset		= genphy_soft_reset,
11398c2ecf20Sopenharmony_ci	.config_aneg		= at803x_config_aneg,
11408c2ecf20Sopenharmony_ci} };
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_cimodule_phy_driver(at803x_driver);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused atheros_tbl[] = {
11458c2ecf20Sopenharmony_ci	{ ATH8030_PHY_ID, AT8030_PHY_ID_MASK },
11468c2ecf20Sopenharmony_ci	{ PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) },
11478c2ecf20Sopenharmony_ci	{ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
11488c2ecf20Sopenharmony_ci	{ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
11498c2ecf20Sopenharmony_ci	{ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
11508c2ecf20Sopenharmony_ci	{ }
11518c2ecf20Sopenharmony_ci};
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, atheros_tbl);
1154