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