162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Motorcomm 8511/8521/8531/8531S PHY driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Peter Geis <pgwipeout@gmail.com> 662306a36Sopenharmony_ci * Author: Frank <Frank.Sae@motor-comm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/etherdevice.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/phy.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define PHY_ID_YT8511 0x0000010a 1662306a36Sopenharmony_ci#define PHY_ID_YT8521 0x0000011a 1762306a36Sopenharmony_ci#define PHY_ID_YT8531 0x4f51e91b 1862306a36Sopenharmony_ci#define PHY_ID_YT8531S 0x4f51e91a 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* YT8521/YT8531S Register Overview 2162306a36Sopenharmony_ci * UTP Register space | FIBER Register space 2262306a36Sopenharmony_ci * ------------------------------------------------------------ 2362306a36Sopenharmony_ci * | UTP MII | FIBER MII | 2462306a36Sopenharmony_ci * | UTP MMD | | 2562306a36Sopenharmony_ci * | UTP Extended | FIBER Extended | 2662306a36Sopenharmony_ci * ------------------------------------------------------------ 2762306a36Sopenharmony_ci * | Common Extended | 2862306a36Sopenharmony_ci * ------------------------------------------------------------ 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Specific Function Control Register */ 3462306a36Sopenharmony_ci#define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG 0x10 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 2b00 Manual MDI configuration 3762306a36Sopenharmony_ci * 2b01 Manual MDIX configuration 3862306a36Sopenharmony_ci * 2b10 Reserved 3962306a36Sopenharmony_ci * 2b11 Enable automatic crossover for all modes *default* 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK (BIT(6) | BIT(5)) 4262306a36Sopenharmony_ci#define YTPHY_SFCR_CROSSOVER_EN BIT(3) 4362306a36Sopenharmony_ci#define YTPHY_SFCR_SQE_TEST_EN BIT(2) 4462306a36Sopenharmony_ci#define YTPHY_SFCR_POLARITY_REVERSAL_EN BIT(1) 4562306a36Sopenharmony_ci#define YTPHY_SFCR_JABBER_DIS BIT(0) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Specific Status Register */ 4862306a36Sopenharmony_ci#define YTPHY_SPECIFIC_STATUS_REG 0x11 4962306a36Sopenharmony_ci#define YTPHY_SSR_SPEED_MODE_OFFSET 14 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14)) 5262306a36Sopenharmony_ci#define YTPHY_SSR_SPEED_10M 0x0 5362306a36Sopenharmony_ci#define YTPHY_SSR_SPEED_100M 0x1 5462306a36Sopenharmony_ci#define YTPHY_SSR_SPEED_1000M 0x2 5562306a36Sopenharmony_ci#define YTPHY_SSR_DUPLEX_OFFSET 13 5662306a36Sopenharmony_ci#define YTPHY_SSR_DUPLEX BIT(13) 5762306a36Sopenharmony_ci#define YTPHY_SSR_PAGE_RECEIVED BIT(12) 5862306a36Sopenharmony_ci#define YTPHY_SSR_SPEED_DUPLEX_RESOLVED BIT(11) 5962306a36Sopenharmony_ci#define YTPHY_SSR_LINK BIT(10) 6062306a36Sopenharmony_ci#define YTPHY_SSR_MDIX_CROSSOVER BIT(6) 6162306a36Sopenharmony_ci#define YTPHY_SSR_DOWNGRADE BIT(5) 6262306a36Sopenharmony_ci#define YTPHY_SSR_TRANSMIT_PAUSE BIT(3) 6362306a36Sopenharmony_ci#define YTPHY_SSR_RECEIVE_PAUSE BIT(2) 6462306a36Sopenharmony_ci#define YTPHY_SSR_POLARITY BIT(1) 6562306a36Sopenharmony_ci#define YTPHY_SSR_JABBER BIT(0) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Interrupt enable Register */ 6862306a36Sopenharmony_ci#define YTPHY_INTERRUPT_ENABLE_REG 0x12 6962306a36Sopenharmony_ci#define YTPHY_IER_WOL BIT(6) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Interrupt Status Register */ 7262306a36Sopenharmony_ci#define YTPHY_INTERRUPT_STATUS_REG 0x13 7362306a36Sopenharmony_ci#define YTPHY_ISR_AUTONEG_ERR BIT(15) 7462306a36Sopenharmony_ci#define YTPHY_ISR_SPEED_CHANGED BIT(14) 7562306a36Sopenharmony_ci#define YTPHY_ISR_DUPLEX_CHANGED BIT(13) 7662306a36Sopenharmony_ci#define YTPHY_ISR_PAGE_RECEIVED BIT(12) 7762306a36Sopenharmony_ci#define YTPHY_ISR_LINK_FAILED BIT(11) 7862306a36Sopenharmony_ci#define YTPHY_ISR_LINK_SUCCESSED BIT(10) 7962306a36Sopenharmony_ci#define YTPHY_ISR_WOL BIT(6) 8062306a36Sopenharmony_ci#define YTPHY_ISR_WIRESPEED_DOWNGRADE BIT(5) 8162306a36Sopenharmony_ci#define YTPHY_ISR_SERDES_LINK_FAILED BIT(3) 8262306a36Sopenharmony_ci#define YTPHY_ISR_SERDES_LINK_SUCCESSED BIT(2) 8362306a36Sopenharmony_ci#define YTPHY_ISR_POLARITY_CHANGED BIT(1) 8462306a36Sopenharmony_ci#define YTPHY_ISR_JABBER_HAPPENED BIT(0) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Speed Auto Downgrade Control Register */ 8762306a36Sopenharmony_ci#define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG 0x14 8862306a36Sopenharmony_ci#define YTPHY_SADCR_SPEED_DOWNGRADE_EN BIT(5) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* If these bits are set to 3, the PHY attempts five times ( 3(set value) + 9162306a36Sopenharmony_ci * additional 2) before downgrading, default 0x3 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci#define YTPHY_SADCR_SPEED_RETRY_LIMIT (0x3 << 2) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Rx Error Counter Register */ 9662306a36Sopenharmony_ci#define YTPHY_RX_ERROR_COUNTER_REG 0x15 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* Extended Register's Address Offset Register */ 9962306a36Sopenharmony_ci#define YTPHY_PAGE_SELECT 0x1E 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Extended Register's Data Register */ 10262306a36Sopenharmony_ci#define YTPHY_PAGE_DATA 0x1F 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* FIBER Auto-Negotiation link partner ability */ 10562306a36Sopenharmony_ci#define YTPHY_FLPA_PAUSE (0x3 << 7) 10662306a36Sopenharmony_ci#define YTPHY_FLPA_ASYM_PAUSE (0x2 << 7) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define YT8511_PAGE_SELECT 0x1e 10962306a36Sopenharmony_ci#define YT8511_PAGE 0x1f 11062306a36Sopenharmony_ci#define YT8511_EXT_CLK_GATE 0x0c 11162306a36Sopenharmony_ci#define YT8511_EXT_DELAY_DRIVE 0x0d 11262306a36Sopenharmony_ci#define YT8511_EXT_SLEEP_CTRL 0x27 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 2b00 25m from pll 11562306a36Sopenharmony_ci * 2b01 25m from xtl *default* 11662306a36Sopenharmony_ci * 2b10 62.m from pll 11762306a36Sopenharmony_ci * 2b11 125m from pll 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci#define YT8511_CLK_125M (BIT(2) | BIT(1)) 12062306a36Sopenharmony_ci#define YT8511_PLLON_SLP BIT(14) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */ 12362306a36Sopenharmony_ci#define YT8511_DELAY_RX BIT(0) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* TX Gig-E Delay is bits 7:4, default 0x5 12662306a36Sopenharmony_ci * TX Fast-E Delay is bits 15:12, default 0xf 12762306a36Sopenharmony_ci * Delay = 150ps * N - 250ps 12862306a36Sopenharmony_ci * On = 2000ps, off = 50ps 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci#define YT8511_DELAY_GE_TX_EN (0xf << 4) 13162306a36Sopenharmony_ci#define YT8511_DELAY_GE_TX_DIS (0x2 << 4) 13262306a36Sopenharmony_ci#define YT8511_DELAY_FE_TX_EN (0xf << 12) 13362306a36Sopenharmony_ci#define YT8511_DELAY_FE_TX_DIS (0x2 << 12) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Extended register is different from MMD Register and MII Register. 13662306a36Sopenharmony_ci * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to 13762306a36Sopenharmony_ci * operate extended register. 13862306a36Sopenharmony_ci * Extended Register start 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Phy gmii clock gating Register */ 14262306a36Sopenharmony_ci#define YT8521_CLOCK_GATING_REG 0xC 14362306a36Sopenharmony_ci#define YT8521_CGR_RX_CLK_EN BIT(12) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define YT8521_EXTREG_SLEEP_CONTROL1_REG 0x27 14662306a36Sopenharmony_ci#define YT8521_ESC1R_SLEEP_SW BIT(15) 14762306a36Sopenharmony_ci#define YT8521_ESC1R_PLLON_SLP BIT(14) 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Phy fiber Link timer cfg2 Register */ 15062306a36Sopenharmony_ci#define YT8521_LINK_TIMER_CFG2_REG 0xA5 15162306a36Sopenharmony_ci#define YT8521_LTCR_EN_AUTOSEN BIT(15) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 0xA000, 0xA001, 0xA003, 0xA006 ~ 0xA00A and 0xA012 are common ext registers 15462306a36Sopenharmony_ci * of yt8521 phy. There is no need to switch reg space when operating these 15562306a36Sopenharmony_ci * registers. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define YT8521_REG_SPACE_SELECT_REG 0xA000 15962306a36Sopenharmony_ci#define YT8521_RSSR_SPACE_MASK BIT(1) 16062306a36Sopenharmony_ci#define YT8521_RSSR_FIBER_SPACE (0x1 << 1) 16162306a36Sopenharmony_ci#define YT8521_RSSR_UTP_SPACE (0x0 << 1) 16262306a36Sopenharmony_ci#define YT8521_RSSR_TO_BE_ARBITRATED (0xFF) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define YT8521_CHIP_CONFIG_REG 0xA001 16562306a36Sopenharmony_ci#define YT8521_CCR_SW_RST BIT(15) 16662306a36Sopenharmony_ci#define YT8531_RGMII_LDO_VOL_MASK GENMASK(5, 4) 16762306a36Sopenharmony_ci#define YT8531_LDO_VOL_3V3 0x0 16862306a36Sopenharmony_ci#define YT8531_LDO_VOL_1V8 0x2 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 1b0 disable 1.9ns rxc clock delay *default* 17162306a36Sopenharmony_ci * 1b1 enable 1.9ns rxc clock delay 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci#define YT8521_CCR_RXC_DLY_EN BIT(8) 17462306a36Sopenharmony_ci#define YT8521_CCR_RXC_DLY_1_900_NS 1900 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci#define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0)) 17762306a36Sopenharmony_ci#define YT8521_CCR_MODE_UTP_TO_RGMII 0 17862306a36Sopenharmony_ci#define YT8521_CCR_MODE_FIBER_TO_RGMII 1 17962306a36Sopenharmony_ci#define YT8521_CCR_MODE_UTP_FIBER_TO_RGMII 2 18062306a36Sopenharmony_ci#define YT8521_CCR_MODE_UTP_TO_SGMII 3 18162306a36Sopenharmony_ci#define YT8521_CCR_MODE_SGPHY_TO_RGMAC 4 18262306a36Sopenharmony_ci#define YT8521_CCR_MODE_SGMAC_TO_RGPHY 5 18362306a36Sopenharmony_ci#define YT8521_CCR_MODE_UTP_TO_FIBER_AUTO 6 18462306a36Sopenharmony_ci#define YT8521_CCR_MODE_UTP_TO_FIBER_FORCE 7 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 3 phy polling modes,poll mode combines utp and fiber mode*/ 18762306a36Sopenharmony_ci#define YT8521_MODE_FIBER 0x1 18862306a36Sopenharmony_ci#define YT8521_MODE_UTP 0x2 18962306a36Sopenharmony_ci#define YT8521_MODE_POLL 0x3 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#define YT8521_RGMII_CONFIG1_REG 0xA003 19262306a36Sopenharmony_ci/* 1b0 use original tx_clk_rgmii *default* 19362306a36Sopenharmony_ci * 1b1 use inverted tx_clk_rgmii. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci#define YT8521_RC1R_TX_CLK_SEL_INVERTED BIT(14) 19662306a36Sopenharmony_ci#define YT8521_RC1R_RX_DELAY_MASK GENMASK(13, 10) 19762306a36Sopenharmony_ci#define YT8521_RC1R_FE_TX_DELAY_MASK GENMASK(7, 4) 19862306a36Sopenharmony_ci#define YT8521_RC1R_GE_TX_DELAY_MASK GENMASK(3, 0) 19962306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_0_000_NS 0 20062306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_0_150_NS 1 20162306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_0_300_NS 2 20262306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_0_450_NS 3 20362306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_0_600_NS 4 20462306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_0_750_NS 5 20562306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_0_900_NS 6 20662306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_1_050_NS 7 20762306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_1_200_NS 8 20862306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_1_350_NS 9 20962306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_1_500_NS 10 21062306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_1_650_NS 11 21162306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_1_800_NS 12 21262306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_1_950_NS 13 21362306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_2_100_NS 14 21462306a36Sopenharmony_ci#define YT8521_RC1R_RGMII_2_250_NS 15 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#define YTPHY_MISC_CONFIG_REG 0xA006 21762306a36Sopenharmony_ci#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) 21862306a36Sopenharmony_ci#define YTPHY_MCR_FIBER_1000BX (0x1 << 0) 21962306a36Sopenharmony_ci#define YTPHY_MCR_FIBER_100FX (0x0 << 0) 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* WOL MAC ADDR: MACADDR2(highest), MACADDR1(middle), MACADDR0(lowest) */ 22262306a36Sopenharmony_ci#define YTPHY_WOL_MACADDR2_REG 0xA007 22362306a36Sopenharmony_ci#define YTPHY_WOL_MACADDR1_REG 0xA008 22462306a36Sopenharmony_ci#define YTPHY_WOL_MACADDR0_REG 0xA009 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci#define YTPHY_WOL_CONFIG_REG 0xA00A 22762306a36Sopenharmony_ci#define YTPHY_WCR_INTR_SEL BIT(6) 22862306a36Sopenharmony_ci#define YTPHY_WCR_ENABLE BIT(3) 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 2b00 84ms 23162306a36Sopenharmony_ci * 2b01 168ms *default* 23262306a36Sopenharmony_ci * 2b10 336ms 23362306a36Sopenharmony_ci * 2b11 672ms 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci#define YTPHY_WCR_PULSE_WIDTH_MASK (BIT(2) | BIT(1)) 23662306a36Sopenharmony_ci#define YTPHY_WCR_PULSE_WIDTH_672MS (BIT(2) | BIT(1)) 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 1b0 Interrupt and WOL events is level triggered and active LOW *default* 23962306a36Sopenharmony_ci * 1b1 Interrupt and WOL events is pulse triggered and active LOW 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci#define YTPHY_WCR_TYPE_PULSE BIT(0) 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci#define YTPHY_PAD_DRIVE_STRENGTH_REG 0xA010 24462306a36Sopenharmony_ci#define YT8531_RGMII_RXC_DS_MASK GENMASK(15, 13) 24562306a36Sopenharmony_ci#define YT8531_RGMII_RXD_DS_HI_MASK BIT(12) /* Bit 2 of rxd_ds */ 24662306a36Sopenharmony_ci#define YT8531_RGMII_RXD_DS_LOW_MASK GENMASK(5, 4) /* Bit 1/0 of rxd_ds */ 24762306a36Sopenharmony_ci#define YT8531_RGMII_RX_DS_DEFAULT 0x3 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci#define YTPHY_SYNCE_CFG_REG 0xA012 25062306a36Sopenharmony_ci#define YT8521_SCR_SYNCE_ENABLE BIT(5) 25162306a36Sopenharmony_ci/* 1b0 output 25m clock 25262306a36Sopenharmony_ci * 1b1 output 125m clock *default* 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci#define YT8521_SCR_CLK_FRE_SEL_125M BIT(3) 25562306a36Sopenharmony_ci#define YT8521_SCR_CLK_SRC_MASK GENMASK(2, 1) 25662306a36Sopenharmony_ci#define YT8521_SCR_CLK_SRC_PLL_125M 0 25762306a36Sopenharmony_ci#define YT8521_SCR_CLK_SRC_UTP_RX 1 25862306a36Sopenharmony_ci#define YT8521_SCR_CLK_SRC_SDS_RX 2 25962306a36Sopenharmony_ci#define YT8521_SCR_CLK_SRC_REF_25M 3 26062306a36Sopenharmony_ci#define YT8531_SCR_SYNCE_ENABLE BIT(6) 26162306a36Sopenharmony_ci/* 1b0 output 25m clock *default* 26262306a36Sopenharmony_ci * 1b1 output 125m clock 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci#define YT8531_SCR_CLK_FRE_SEL_125M BIT(4) 26562306a36Sopenharmony_ci#define YT8531_SCR_CLK_SRC_MASK GENMASK(3, 1) 26662306a36Sopenharmony_ci#define YT8531_SCR_CLK_SRC_PLL_125M 0 26762306a36Sopenharmony_ci#define YT8531_SCR_CLK_SRC_UTP_RX 1 26862306a36Sopenharmony_ci#define YT8531_SCR_CLK_SRC_SDS_RX 2 26962306a36Sopenharmony_ci#define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL 3 27062306a36Sopenharmony_ci#define YT8531_SCR_CLK_SRC_REF_25M 4 27162306a36Sopenharmony_ci#define YT8531_SCR_CLK_SRC_SSC_25M 5 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* Extended Register end */ 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci#define YTPHY_DTS_OUTPUT_CLK_DIS 0 27662306a36Sopenharmony_ci#define YTPHY_DTS_OUTPUT_CLK_25M 25000000 27762306a36Sopenharmony_ci#define YTPHY_DTS_OUTPUT_CLK_125M 125000000 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistruct yt8521_priv { 28062306a36Sopenharmony_ci /* combo_advertising is used for case of YT8521 in combo mode, 28162306a36Sopenharmony_ci * this means that yt8521 may work in utp or fiber mode which depends 28262306a36Sopenharmony_ci * on which media is connected (YT8521_RSSR_TO_BE_ARBITRATED). 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(combo_advertising); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* YT8521_MODE_FIBER / YT8521_MODE_UTP / YT8521_MODE_POLL*/ 28762306a36Sopenharmony_ci u8 polling_mode; 28862306a36Sopenharmony_ci u8 strap_mode; /* 8 working modes */ 28962306a36Sopenharmony_ci /* current reg page of yt8521 phy: 29062306a36Sopenharmony_ci * YT8521_RSSR_UTP_SPACE 29162306a36Sopenharmony_ci * YT8521_RSSR_FIBER_SPACE 29262306a36Sopenharmony_ci * YT8521_RSSR_TO_BE_ARBITRATED 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci u8 reg_page; 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/** 29862306a36Sopenharmony_ci * ytphy_read_ext() - read a PHY's extended register 29962306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 30062306a36Sopenharmony_ci * @regnum: register number to read 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * NOTE:The caller must have taken the MDIO bus lock. 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * returns the value of regnum reg or negative error code 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_cistatic int ytphy_read_ext(struct phy_device *phydev, u16 regnum) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci int ret; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); 31162306a36Sopenharmony_ci if (ret < 0) 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return __phy_read(phydev, YTPHY_PAGE_DATA); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/** 31862306a36Sopenharmony_ci * ytphy_read_ext_with_lock() - read a PHY's extended register 31962306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 32062306a36Sopenharmony_ci * @regnum: register number to read 32162306a36Sopenharmony_ci * 32262306a36Sopenharmony_ci * returns the value of regnum reg or negative error code 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_cistatic int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci int ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 32962306a36Sopenharmony_ci ret = ytphy_read_ext(phydev, regnum); 33062306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return ret; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/** 33662306a36Sopenharmony_ci * ytphy_write_ext() - write a PHY's extended register 33762306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 33862306a36Sopenharmony_ci * @regnum: register number to write 33962306a36Sopenharmony_ci * @val: value to write to @regnum 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * NOTE:The caller must have taken the MDIO bus lock. 34262306a36Sopenharmony_ci * 34362306a36Sopenharmony_ci * returns 0 or negative error code 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_cistatic int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int ret; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); 35062306a36Sopenharmony_ci if (ret < 0) 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return __phy_write(phydev, YTPHY_PAGE_DATA, val); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/** 35762306a36Sopenharmony_ci * ytphy_write_ext_with_lock() - write a PHY's extended register 35862306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 35962306a36Sopenharmony_ci * @regnum: register number to write 36062306a36Sopenharmony_ci * @val: value to write to @regnum 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * returns 0 or negative error code 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_cistatic int ytphy_write_ext_with_lock(struct phy_device *phydev, u16 regnum, 36562306a36Sopenharmony_ci u16 val) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 37062306a36Sopenharmony_ci ret = ytphy_write_ext(phydev, regnum, val); 37162306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/** 37762306a36Sopenharmony_ci * ytphy_modify_ext() - bits modify a PHY's extended register 37862306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 37962306a36Sopenharmony_ci * @regnum: register number to write 38062306a36Sopenharmony_ci * @mask: bit mask of bits to clear 38162306a36Sopenharmony_ci * @set: bit mask of bits to set 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * NOTE: Convenience function which allows a PHY's extended register to be 38462306a36Sopenharmony_ci * modified as new register value = (old register value & ~mask) | set. 38562306a36Sopenharmony_ci * The caller must have taken the MDIO bus lock. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * returns 0 or negative error code 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask, 39062306a36Sopenharmony_ci u16 set) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int ret; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); 39562306a36Sopenharmony_ci if (ret < 0) 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return __phy_modify(phydev, YTPHY_PAGE_DATA, mask, set); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/** 40262306a36Sopenharmony_ci * ytphy_modify_ext_with_lock() - bits modify a PHY's extended register 40362306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 40462306a36Sopenharmony_ci * @regnum: register number to write 40562306a36Sopenharmony_ci * @mask: bit mask of bits to clear 40662306a36Sopenharmony_ci * @set: bit mask of bits to set 40762306a36Sopenharmony_ci * 40862306a36Sopenharmony_ci * NOTE: Convenience function which allows a PHY's extended register to be 40962306a36Sopenharmony_ci * modified as new register value = (old register value & ~mask) | set. 41062306a36Sopenharmony_ci * 41162306a36Sopenharmony_ci * returns 0 or negative error code 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_cistatic int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum, 41462306a36Sopenharmony_ci u16 mask, u16 set) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci int ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 41962306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, regnum, mask, set); 42062306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return ret; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/** 42662306a36Sopenharmony_ci * ytphy_get_wol() - report whether wake-on-lan is enabled 42762306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 42862306a36Sopenharmony_ci * @wol: a pointer to a &struct ethtool_wolinfo 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * NOTE: YTPHY_WOL_CONFIG_REG is common ext reg. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_cistatic void ytphy_get_wol(struct phy_device *phydev, 43362306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci int wol_config; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci wol->supported = WAKE_MAGIC; 43862306a36Sopenharmony_ci wol->wolopts = 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); 44162306a36Sopenharmony_ci if (wol_config < 0) 44262306a36Sopenharmony_ci return; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (wol_config & YTPHY_WCR_ENABLE) 44562306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/** 44962306a36Sopenharmony_ci * ytphy_set_wol() - turn wake-on-lan on or off 45062306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 45162306a36Sopenharmony_ci * @wol: a pointer to a &struct ethtool_wolinfo 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci * NOTE: YTPHY_WOL_CONFIG_REG, YTPHY_WOL_MACADDR2_REG, YTPHY_WOL_MACADDR1_REG 45462306a36Sopenharmony_ci * and YTPHY_WOL_MACADDR0_REG are common ext reg. The 45562306a36Sopenharmony_ci * YTPHY_INTERRUPT_ENABLE_REG of UTP is special, fiber also use this register. 45662306a36Sopenharmony_ci * 45762306a36Sopenharmony_ci * returns 0 or negative errno code 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_cistatic int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct net_device *p_attached_dev; 46262306a36Sopenharmony_ci const u16 mac_addr_reg[] = { 46362306a36Sopenharmony_ci YTPHY_WOL_MACADDR2_REG, 46462306a36Sopenharmony_ci YTPHY_WOL_MACADDR1_REG, 46562306a36Sopenharmony_ci YTPHY_WOL_MACADDR0_REG, 46662306a36Sopenharmony_ci }; 46762306a36Sopenharmony_ci const u8 *mac_addr; 46862306a36Sopenharmony_ci int old_page; 46962306a36Sopenharmony_ci int ret = 0; 47062306a36Sopenharmony_ci u16 mask; 47162306a36Sopenharmony_ci u16 val; 47262306a36Sopenharmony_ci u8 i; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 47562306a36Sopenharmony_ci p_attached_dev = phydev->attached_dev; 47662306a36Sopenharmony_ci if (!p_attached_dev) 47762306a36Sopenharmony_ci return -ENODEV; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci mac_addr = (const u8 *)p_attached_dev->dev_addr; 48062306a36Sopenharmony_ci if (!is_valid_ether_addr(mac_addr)) 48162306a36Sopenharmony_ci return -EINVAL; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* lock mdio bus then switch to utp reg space */ 48462306a36Sopenharmony_ci old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); 48562306a36Sopenharmony_ci if (old_page < 0) 48662306a36Sopenharmony_ci goto err_restore_page; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Store the device address for the magic packet */ 48962306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 49062306a36Sopenharmony_ci ret = ytphy_write_ext(phydev, mac_addr_reg[i], 49162306a36Sopenharmony_ci ((mac_addr[i * 2] << 8)) | 49262306a36Sopenharmony_ci (mac_addr[i * 2 + 1])); 49362306a36Sopenharmony_ci if (ret < 0) 49462306a36Sopenharmony_ci goto err_restore_page; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* Enable WOL feature */ 49862306a36Sopenharmony_ci mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL; 49962306a36Sopenharmony_ci val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; 50062306a36Sopenharmony_ci val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS; 50162306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val); 50262306a36Sopenharmony_ci if (ret < 0) 50362306a36Sopenharmony_ci goto err_restore_page; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Enable WOL interrupt */ 50662306a36Sopenharmony_ci ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0, 50762306a36Sopenharmony_ci YTPHY_IER_WOL); 50862306a36Sopenharmony_ci if (ret < 0) 50962306a36Sopenharmony_ci goto err_restore_page; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci } else { 51262306a36Sopenharmony_ci old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); 51362306a36Sopenharmony_ci if (old_page < 0) 51462306a36Sopenharmony_ci goto err_restore_page; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Disable WOL feature */ 51762306a36Sopenharmony_ci mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; 51862306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Disable WOL interrupt */ 52162306a36Sopenharmony_ci ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 52262306a36Sopenharmony_ci YTPHY_IER_WOL, 0); 52362306a36Sopenharmony_ci if (ret < 0) 52462306a36Sopenharmony_ci goto err_restore_page; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cierr_restore_page: 52862306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, ret); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int yt8531_set_wol(struct phy_device *phydev, 53262306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci const u16 mac_addr_reg[] = { 53562306a36Sopenharmony_ci YTPHY_WOL_MACADDR2_REG, 53662306a36Sopenharmony_ci YTPHY_WOL_MACADDR1_REG, 53762306a36Sopenharmony_ci YTPHY_WOL_MACADDR0_REG, 53862306a36Sopenharmony_ci }; 53962306a36Sopenharmony_ci const u8 *mac_addr; 54062306a36Sopenharmony_ci u16 mask, val; 54162306a36Sopenharmony_ci int ret; 54262306a36Sopenharmony_ci u8 i; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 54562306a36Sopenharmony_ci mac_addr = phydev->attached_dev->dev_addr; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Store the device address for the magic packet */ 54862306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 54962306a36Sopenharmony_ci ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i], 55062306a36Sopenharmony_ci ((mac_addr[i * 2] << 8)) | 55162306a36Sopenharmony_ci (mac_addr[i * 2 + 1])); 55262306a36Sopenharmony_ci if (ret < 0) 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* Enable WOL feature */ 55762306a36Sopenharmony_ci mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL; 55862306a36Sopenharmony_ci val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; 55962306a36Sopenharmony_ci val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS; 56062306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG, 56162306a36Sopenharmony_ci mask, val); 56262306a36Sopenharmony_ci if (ret < 0) 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Enable WOL interrupt */ 56662306a36Sopenharmony_ci ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0, 56762306a36Sopenharmony_ci YTPHY_IER_WOL); 56862306a36Sopenharmony_ci if (ret < 0) 56962306a36Sopenharmony_ci return ret; 57062306a36Sopenharmony_ci } else { 57162306a36Sopenharmony_ci /* Disable WOL feature */ 57262306a36Sopenharmony_ci mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; 57362306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG, 57462306a36Sopenharmony_ci mask, 0); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Disable WOL interrupt */ 57762306a36Sopenharmony_ci ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 57862306a36Sopenharmony_ci YTPHY_IER_WOL, 0); 57962306a36Sopenharmony_ci if (ret < 0) 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int yt8511_read_page(struct phy_device *phydev) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci return __phy_read(phydev, YT8511_PAGE_SELECT); 58962306a36Sopenharmony_ci}; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int yt8511_write_page(struct phy_device *phydev, int page) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci return __phy_write(phydev, YT8511_PAGE_SELECT, page); 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int yt8511_config_init(struct phy_device *phydev) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci int oldpage, ret = 0; 59962306a36Sopenharmony_ci unsigned int ge, fe; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE); 60262306a36Sopenharmony_ci if (oldpage < 0) 60362306a36Sopenharmony_ci goto err_restore_page; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* set rgmii delay mode */ 60662306a36Sopenharmony_ci switch (phydev->interface) { 60762306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 60862306a36Sopenharmony_ci ge = YT8511_DELAY_GE_TX_DIS; 60962306a36Sopenharmony_ci fe = YT8511_DELAY_FE_TX_DIS; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 61262306a36Sopenharmony_ci ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS; 61362306a36Sopenharmony_ci fe = YT8511_DELAY_FE_TX_DIS; 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 61662306a36Sopenharmony_ci ge = YT8511_DELAY_GE_TX_EN; 61762306a36Sopenharmony_ci fe = YT8511_DELAY_FE_TX_EN; 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 62062306a36Sopenharmony_ci ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN; 62162306a36Sopenharmony_ci fe = YT8511_DELAY_FE_TX_EN; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci default: /* do not support other modes */ 62462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 62562306a36Sopenharmony_ci goto err_restore_page; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = __phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge); 62962306a36Sopenharmony_ci if (ret < 0) 63062306a36Sopenharmony_ci goto err_restore_page; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* set clock mode to 125mhz */ 63362306a36Sopenharmony_ci ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M); 63462306a36Sopenharmony_ci if (ret < 0) 63562306a36Sopenharmony_ci goto err_restore_page; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* fast ethernet delay is in a separate page */ 63862306a36Sopenharmony_ci ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE); 63962306a36Sopenharmony_ci if (ret < 0) 64062306a36Sopenharmony_ci goto err_restore_page; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ret = __phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe); 64362306a36Sopenharmony_ci if (ret < 0) 64462306a36Sopenharmony_ci goto err_restore_page; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* leave pll enabled in sleep */ 64762306a36Sopenharmony_ci ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL); 64862306a36Sopenharmony_ci if (ret < 0) 64962306a36Sopenharmony_ci goto err_restore_page; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP); 65262306a36Sopenharmony_ci if (ret < 0) 65362306a36Sopenharmony_ci goto err_restore_page; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cierr_restore_page: 65662306a36Sopenharmony_ci return phy_restore_page(phydev, oldpage, ret); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci/** 66062306a36Sopenharmony_ci * yt8521_read_page() - read reg page 66162306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 66262306a36Sopenharmony_ci * 66362306a36Sopenharmony_ci * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE/ 66462306a36Sopenharmony_ci * YT8521_RSSR_UTP_SPACE) or negative errno code 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_cistatic int yt8521_read_page(struct phy_device *phydev) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci int old_page; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci old_page = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG); 67162306a36Sopenharmony_ci if (old_page < 0) 67262306a36Sopenharmony_ci return old_page; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if ((old_page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE) 67562306a36Sopenharmony_ci return YT8521_RSSR_FIBER_SPACE; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return YT8521_RSSR_UTP_SPACE; 67862306a36Sopenharmony_ci}; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci/** 68162306a36Sopenharmony_ci * yt8521_write_page() - write reg page 68262306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 68362306a36Sopenharmony_ci * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write. 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * returns 0 or negative errno code 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_cistatic int yt8521_write_page(struct phy_device *phydev, int page) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci int mask = YT8521_RSSR_SPACE_MASK; 69062306a36Sopenharmony_ci int set; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if ((page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE) 69362306a36Sopenharmony_ci set = YT8521_RSSR_FIBER_SPACE; 69462306a36Sopenharmony_ci else 69562306a36Sopenharmony_ci set = YT8521_RSSR_UTP_SPACE; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set); 69862306a36Sopenharmony_ci}; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/** 70162306a36Sopenharmony_ci * struct ytphy_cfg_reg_map - map a config value to a register value 70262306a36Sopenharmony_ci * @cfg: value in device configuration 70362306a36Sopenharmony_ci * @reg: value in the register 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistruct ytphy_cfg_reg_map { 70662306a36Sopenharmony_ci u32 cfg; 70762306a36Sopenharmony_ci u32 reg; 70862306a36Sopenharmony_ci}; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = { 71162306a36Sopenharmony_ci /* for tx delay / rx delay with YT8521_CCR_RXC_DLY_EN is not set. */ 71262306a36Sopenharmony_ci { 0, YT8521_RC1R_RGMII_0_000_NS }, 71362306a36Sopenharmony_ci { 150, YT8521_RC1R_RGMII_0_150_NS }, 71462306a36Sopenharmony_ci { 300, YT8521_RC1R_RGMII_0_300_NS }, 71562306a36Sopenharmony_ci { 450, YT8521_RC1R_RGMII_0_450_NS }, 71662306a36Sopenharmony_ci { 600, YT8521_RC1R_RGMII_0_600_NS }, 71762306a36Sopenharmony_ci { 750, YT8521_RC1R_RGMII_0_750_NS }, 71862306a36Sopenharmony_ci { 900, YT8521_RC1R_RGMII_0_900_NS }, 71962306a36Sopenharmony_ci { 1050, YT8521_RC1R_RGMII_1_050_NS }, 72062306a36Sopenharmony_ci { 1200, YT8521_RC1R_RGMII_1_200_NS }, 72162306a36Sopenharmony_ci { 1350, YT8521_RC1R_RGMII_1_350_NS }, 72262306a36Sopenharmony_ci { 1500, YT8521_RC1R_RGMII_1_500_NS }, 72362306a36Sopenharmony_ci { 1650, YT8521_RC1R_RGMII_1_650_NS }, 72462306a36Sopenharmony_ci { 1800, YT8521_RC1R_RGMII_1_800_NS }, 72562306a36Sopenharmony_ci { 1950, YT8521_RC1R_RGMII_1_950_NS }, /* default tx/rx delay */ 72662306a36Sopenharmony_ci { 2100, YT8521_RC1R_RGMII_2_100_NS }, 72762306a36Sopenharmony_ci { 2250, YT8521_RC1R_RGMII_2_250_NS }, 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* only for rx delay with YT8521_CCR_RXC_DLY_EN is set. */ 73062306a36Sopenharmony_ci { 0 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_000_NS }, 73162306a36Sopenharmony_ci { 150 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_150_NS }, 73262306a36Sopenharmony_ci { 300 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_300_NS }, 73362306a36Sopenharmony_ci { 450 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_450_NS }, 73462306a36Sopenharmony_ci { 600 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_600_NS }, 73562306a36Sopenharmony_ci { 750 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_750_NS }, 73662306a36Sopenharmony_ci { 900 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_900_NS }, 73762306a36Sopenharmony_ci { 1050 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_050_NS }, 73862306a36Sopenharmony_ci { 1200 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_200_NS }, 73962306a36Sopenharmony_ci { 1350 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_350_NS }, 74062306a36Sopenharmony_ci { 1500 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_500_NS }, 74162306a36Sopenharmony_ci { 1650 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_650_NS }, 74262306a36Sopenharmony_ci { 1800 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_800_NS }, 74362306a36Sopenharmony_ci { 1950 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_950_NS }, 74462306a36Sopenharmony_ci { 2100 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_100_NS }, 74562306a36Sopenharmony_ci { 2250 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_250_NS } 74662306a36Sopenharmony_ci}; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic u32 ytphy_get_delay_reg_value(struct phy_device *phydev, 74962306a36Sopenharmony_ci const char *prop_name, 75062306a36Sopenharmony_ci const struct ytphy_cfg_reg_map *tbl, 75162306a36Sopenharmony_ci int tb_size, 75262306a36Sopenharmony_ci u16 *rxc_dly_en, 75362306a36Sopenharmony_ci u32 dflt) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct device_node *node = phydev->mdio.dev.of_node; 75662306a36Sopenharmony_ci int tb_size_half = tb_size / 2; 75762306a36Sopenharmony_ci u32 val; 75862306a36Sopenharmony_ci int i; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (of_property_read_u32(node, prop_name, &val)) 76162306a36Sopenharmony_ci goto err_dts_val; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* when rxc_dly_en is NULL, it is get the delay for tx, only half of 76462306a36Sopenharmony_ci * tb_size is valid. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci if (!rxc_dly_en) 76762306a36Sopenharmony_ci tb_size = tb_size_half; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci for (i = 0; i < tb_size; i++) { 77062306a36Sopenharmony_ci if (tbl[i].cfg == val) { 77162306a36Sopenharmony_ci if (rxc_dly_en && i < tb_size_half) 77262306a36Sopenharmony_ci *rxc_dly_en = 0; 77362306a36Sopenharmony_ci return tbl[i].reg; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci phydev_warn(phydev, "Unsupported value %d for %s using default (%u)\n", 77862306a36Sopenharmony_ci val, prop_name, dflt); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cierr_dts_val: 78162306a36Sopenharmony_ci /* when rxc_dly_en is not NULL, it is get the delay for rx. 78262306a36Sopenharmony_ci * The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps, 78362306a36Sopenharmony_ci * so YT8521_CCR_RXC_DLY_EN should not be set. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci if (rxc_dly_en) 78662306a36Sopenharmony_ci *rxc_dly_en = 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return dflt; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int ytphy_rgmii_clk_delay_config(struct phy_device *phydev) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci int tb_size = ARRAY_SIZE(ytphy_rgmii_delays); 79462306a36Sopenharmony_ci u16 rxc_dly_en = YT8521_CCR_RXC_DLY_EN; 79562306a36Sopenharmony_ci u32 rx_reg, tx_reg; 79662306a36Sopenharmony_ci u16 mask, val = 0; 79762306a36Sopenharmony_ci int ret; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci rx_reg = ytphy_get_delay_reg_value(phydev, "rx-internal-delay-ps", 80062306a36Sopenharmony_ci ytphy_rgmii_delays, tb_size, 80162306a36Sopenharmony_ci &rxc_dly_en, 80262306a36Sopenharmony_ci YT8521_RC1R_RGMII_1_950_NS); 80362306a36Sopenharmony_ci tx_reg = ytphy_get_delay_reg_value(phydev, "tx-internal-delay-ps", 80462306a36Sopenharmony_ci ytphy_rgmii_delays, tb_size, NULL, 80562306a36Sopenharmony_ci YT8521_RC1R_RGMII_1_950_NS); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci switch (phydev->interface) { 80862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 80962306a36Sopenharmony_ci rxc_dly_en = 0; 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 81262306a36Sopenharmony_ci val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg); 81362306a36Sopenharmony_ci break; 81462306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 81562306a36Sopenharmony_ci rxc_dly_en = 0; 81662306a36Sopenharmony_ci val |= FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg); 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 81962306a36Sopenharmony_ci val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) | 82062306a36Sopenharmony_ci FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg); 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci default: /* do not support other modes */ 82362306a36Sopenharmony_ci return -EOPNOTSUPP; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, 82762306a36Sopenharmony_ci YT8521_CCR_RXC_DLY_EN, rxc_dly_en); 82862306a36Sopenharmony_ci if (ret < 0) 82962306a36Sopenharmony_ci return ret; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* Generally, it is not necessary to adjust YT8521_RC1R_FE_TX_DELAY */ 83262306a36Sopenharmony_ci mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_GE_TX_DELAY_MASK; 83362306a36Sopenharmony_ci return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci int ret; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 84162306a36Sopenharmony_ci ret = ytphy_rgmii_clk_delay_config(phydev); 84262306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return ret; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci/** 84862306a36Sopenharmony_ci * struct ytphy_ldo_vol_map - map a current value to a register value 84962306a36Sopenharmony_ci * @vol: ldo voltage 85062306a36Sopenharmony_ci * @ds: value in the register 85162306a36Sopenharmony_ci * @cur: value in device configuration 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_cistruct ytphy_ldo_vol_map { 85462306a36Sopenharmony_ci u32 vol; 85562306a36Sopenharmony_ci u32 ds; 85662306a36Sopenharmony_ci u32 cur; 85762306a36Sopenharmony_ci}; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic const struct ytphy_ldo_vol_map yt8531_ldo_vol[] = { 86062306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 0, .cur = 1200}, 86162306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 1, .cur = 2100}, 86262306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 2, .cur = 2700}, 86362306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 3, .cur = 2910}, 86462306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 4, .cur = 3110}, 86562306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 5, .cur = 3600}, 86662306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 6, .cur = 3970}, 86762306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_1V8, .ds = 7, .cur = 4350}, 86862306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 0, .cur = 3070}, 86962306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 1, .cur = 4080}, 87062306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 2, .cur = 4370}, 87162306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 3, .cur = 4680}, 87262306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 4, .cur = 5020}, 87362306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 5, .cur = 5450}, 87462306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 6, .cur = 5740}, 87562306a36Sopenharmony_ci {.vol = YT8531_LDO_VOL_3V3, .ds = 7, .cur = 6140}, 87662306a36Sopenharmony_ci}; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic u32 yt8531_get_ldo_vol(struct phy_device *phydev) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci u32 val; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); 88362306a36Sopenharmony_ci val = FIELD_GET(YT8531_RGMII_LDO_VOL_MASK, val); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci return val <= YT8531_LDO_VOL_1V8 ? val : YT8531_LDO_VOL_1V8; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int yt8531_get_ds_map(struct phy_device *phydev, u32 cur) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci u32 vol; 89162306a36Sopenharmony_ci int i; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci vol = yt8531_get_ldo_vol(phydev); 89462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(yt8531_ldo_vol); i++) { 89562306a36Sopenharmony_ci if (yt8531_ldo_vol[i].vol == vol && yt8531_ldo_vol[i].cur == cur) 89662306a36Sopenharmony_ci return yt8531_ldo_vol[i].ds; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return -EINVAL; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int yt8531_set_ds(struct phy_device *phydev) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci struct device_node *node = phydev->mdio.dev.of_node; 90562306a36Sopenharmony_ci u32 ds_field_low, ds_field_hi, val; 90662306a36Sopenharmony_ci int ret, ds; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* set rgmii rx clk driver strength */ 90962306a36Sopenharmony_ci if (!of_property_read_u32(node, "motorcomm,rx-clk-drv-microamp", &val)) { 91062306a36Sopenharmony_ci ds = yt8531_get_ds_map(phydev, val); 91162306a36Sopenharmony_ci if (ds < 0) 91262306a36Sopenharmony_ci return dev_err_probe(&phydev->mdio.dev, ds, 91362306a36Sopenharmony_ci "No matching current value was found.\n"); 91462306a36Sopenharmony_ci } else { 91562306a36Sopenharmony_ci ds = YT8531_RGMII_RX_DS_DEFAULT; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, 91962306a36Sopenharmony_ci YTPHY_PAD_DRIVE_STRENGTH_REG, 92062306a36Sopenharmony_ci YT8531_RGMII_RXC_DS_MASK, 92162306a36Sopenharmony_ci FIELD_PREP(YT8531_RGMII_RXC_DS_MASK, ds)); 92262306a36Sopenharmony_ci if (ret < 0) 92362306a36Sopenharmony_ci return ret; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* set rgmii rx data driver strength */ 92662306a36Sopenharmony_ci if (!of_property_read_u32(node, "motorcomm,rx-data-drv-microamp", &val)) { 92762306a36Sopenharmony_ci ds = yt8531_get_ds_map(phydev, val); 92862306a36Sopenharmony_ci if (ds < 0) 92962306a36Sopenharmony_ci return dev_err_probe(&phydev->mdio.dev, ds, 93062306a36Sopenharmony_ci "No matching current value was found.\n"); 93162306a36Sopenharmony_ci } else { 93262306a36Sopenharmony_ci ds = YT8531_RGMII_RX_DS_DEFAULT; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci ds_field_hi = FIELD_GET(BIT(2), ds); 93662306a36Sopenharmony_ci ds_field_hi = FIELD_PREP(YT8531_RGMII_RXD_DS_HI_MASK, ds_field_hi); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci ds_field_low = FIELD_GET(GENMASK(1, 0), ds); 93962306a36Sopenharmony_ci ds_field_low = FIELD_PREP(YT8531_RGMII_RXD_DS_LOW_MASK, ds_field_low); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, 94262306a36Sopenharmony_ci YTPHY_PAD_DRIVE_STRENGTH_REG, 94362306a36Sopenharmony_ci YT8531_RGMII_RXD_DS_LOW_MASK | YT8531_RGMII_RXD_DS_HI_MASK, 94462306a36Sopenharmony_ci ds_field_low | ds_field_hi); 94562306a36Sopenharmony_ci if (ret < 0) 94662306a36Sopenharmony_ci return ret; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci/** 95262306a36Sopenharmony_ci * yt8521_probe() - read chip config then set suitable polling_mode 95362306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 95462306a36Sopenharmony_ci * 95562306a36Sopenharmony_ci * returns 0 or negative errno code 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_cistatic int yt8521_probe(struct phy_device *phydev) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci struct device_node *node = phydev->mdio.dev.of_node; 96062306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 96162306a36Sopenharmony_ci struct yt8521_priv *priv; 96262306a36Sopenharmony_ci int chip_config; 96362306a36Sopenharmony_ci u16 mask, val; 96462306a36Sopenharmony_ci u32 freq; 96562306a36Sopenharmony_ci int ret; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 96862306a36Sopenharmony_ci if (!priv) 96962306a36Sopenharmony_ci return -ENOMEM; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci phydev->priv = priv; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); 97462306a36Sopenharmony_ci if (chip_config < 0) 97562306a36Sopenharmony_ci return chip_config; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci priv->strap_mode = chip_config & YT8521_CCR_MODE_SEL_MASK; 97862306a36Sopenharmony_ci switch (priv->strap_mode) { 97962306a36Sopenharmony_ci case YT8521_CCR_MODE_FIBER_TO_RGMII: 98062306a36Sopenharmony_ci case YT8521_CCR_MODE_SGPHY_TO_RGMAC: 98162306a36Sopenharmony_ci case YT8521_CCR_MODE_SGMAC_TO_RGPHY: 98262306a36Sopenharmony_ci priv->polling_mode = YT8521_MODE_FIBER; 98362306a36Sopenharmony_ci priv->reg_page = YT8521_RSSR_FIBER_SPACE; 98462306a36Sopenharmony_ci phydev->port = PORT_FIBRE; 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci case YT8521_CCR_MODE_UTP_FIBER_TO_RGMII: 98762306a36Sopenharmony_ci case YT8521_CCR_MODE_UTP_TO_FIBER_AUTO: 98862306a36Sopenharmony_ci case YT8521_CCR_MODE_UTP_TO_FIBER_FORCE: 98962306a36Sopenharmony_ci priv->polling_mode = YT8521_MODE_POLL; 99062306a36Sopenharmony_ci priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED; 99162306a36Sopenharmony_ci phydev->port = PORT_NONE; 99262306a36Sopenharmony_ci break; 99362306a36Sopenharmony_ci case YT8521_CCR_MODE_UTP_TO_SGMII: 99462306a36Sopenharmony_ci case YT8521_CCR_MODE_UTP_TO_RGMII: 99562306a36Sopenharmony_ci priv->polling_mode = YT8521_MODE_UTP; 99662306a36Sopenharmony_ci priv->reg_page = YT8521_RSSR_UTP_SPACE; 99762306a36Sopenharmony_ci phydev->port = PORT_TP; 99862306a36Sopenharmony_ci break; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci /* set default reg space */ 100162306a36Sopenharmony_ci if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { 100262306a36Sopenharmony_ci ret = ytphy_write_ext_with_lock(phydev, 100362306a36Sopenharmony_ci YT8521_REG_SPACE_SELECT_REG, 100462306a36Sopenharmony_ci priv->reg_page); 100562306a36Sopenharmony_ci if (ret < 0) 100662306a36Sopenharmony_ci return ret; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq)) 101062306a36Sopenharmony_ci freq = YTPHY_DTS_OUTPUT_CLK_DIS; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (phydev->drv->phy_id == PHY_ID_YT8521) { 101362306a36Sopenharmony_ci switch (freq) { 101462306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_DIS: 101562306a36Sopenharmony_ci mask = YT8521_SCR_SYNCE_ENABLE; 101662306a36Sopenharmony_ci val = 0; 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_25M: 101962306a36Sopenharmony_ci mask = YT8521_SCR_SYNCE_ENABLE | 102062306a36Sopenharmony_ci YT8521_SCR_CLK_SRC_MASK | 102162306a36Sopenharmony_ci YT8521_SCR_CLK_FRE_SEL_125M; 102262306a36Sopenharmony_ci val = YT8521_SCR_SYNCE_ENABLE | 102362306a36Sopenharmony_ci FIELD_PREP(YT8521_SCR_CLK_SRC_MASK, 102462306a36Sopenharmony_ci YT8521_SCR_CLK_SRC_REF_25M); 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_125M: 102762306a36Sopenharmony_ci mask = YT8521_SCR_SYNCE_ENABLE | 102862306a36Sopenharmony_ci YT8521_SCR_CLK_SRC_MASK | 102962306a36Sopenharmony_ci YT8521_SCR_CLK_FRE_SEL_125M; 103062306a36Sopenharmony_ci val = YT8521_SCR_SYNCE_ENABLE | 103162306a36Sopenharmony_ci YT8521_SCR_CLK_FRE_SEL_125M | 103262306a36Sopenharmony_ci FIELD_PREP(YT8521_SCR_CLK_SRC_MASK, 103362306a36Sopenharmony_ci YT8521_SCR_CLK_SRC_PLL_125M); 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci default: 103662306a36Sopenharmony_ci phydev_warn(phydev, "Freq err:%u\n", freq); 103762306a36Sopenharmony_ci return -EINVAL; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci } else if (phydev->drv->phy_id == PHY_ID_YT8531S) { 104062306a36Sopenharmony_ci switch (freq) { 104162306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_DIS: 104262306a36Sopenharmony_ci mask = YT8531_SCR_SYNCE_ENABLE; 104362306a36Sopenharmony_ci val = 0; 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_25M: 104662306a36Sopenharmony_ci mask = YT8531_SCR_SYNCE_ENABLE | 104762306a36Sopenharmony_ci YT8531_SCR_CLK_SRC_MASK | 104862306a36Sopenharmony_ci YT8531_SCR_CLK_FRE_SEL_125M; 104962306a36Sopenharmony_ci val = YT8531_SCR_SYNCE_ENABLE | 105062306a36Sopenharmony_ci FIELD_PREP(YT8531_SCR_CLK_SRC_MASK, 105162306a36Sopenharmony_ci YT8531_SCR_CLK_SRC_REF_25M); 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_125M: 105462306a36Sopenharmony_ci mask = YT8531_SCR_SYNCE_ENABLE | 105562306a36Sopenharmony_ci YT8531_SCR_CLK_SRC_MASK | 105662306a36Sopenharmony_ci YT8531_SCR_CLK_FRE_SEL_125M; 105762306a36Sopenharmony_ci val = YT8531_SCR_SYNCE_ENABLE | 105862306a36Sopenharmony_ci YT8531_SCR_CLK_FRE_SEL_125M | 105962306a36Sopenharmony_ci FIELD_PREP(YT8531_SCR_CLK_SRC_MASK, 106062306a36Sopenharmony_ci YT8531_SCR_CLK_SRC_PLL_125M); 106162306a36Sopenharmony_ci break; 106262306a36Sopenharmony_ci default: 106362306a36Sopenharmony_ci phydev_warn(phydev, "Freq err:%u\n", freq); 106462306a36Sopenharmony_ci return -EINVAL; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci } else { 106762306a36Sopenharmony_ci phydev_warn(phydev, "PHY id err\n"); 106862306a36Sopenharmony_ci return -EINVAL; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask, 107262306a36Sopenharmony_ci val); 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic int yt8531_probe(struct phy_device *phydev) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct device_node *node = phydev->mdio.dev.of_node; 107862306a36Sopenharmony_ci u16 mask, val; 107962306a36Sopenharmony_ci u32 freq; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq)) 108262306a36Sopenharmony_ci freq = YTPHY_DTS_OUTPUT_CLK_DIS; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci switch (freq) { 108562306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_DIS: 108662306a36Sopenharmony_ci mask = YT8531_SCR_SYNCE_ENABLE; 108762306a36Sopenharmony_ci val = 0; 108862306a36Sopenharmony_ci break; 108962306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_25M: 109062306a36Sopenharmony_ci mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK | 109162306a36Sopenharmony_ci YT8531_SCR_CLK_FRE_SEL_125M; 109262306a36Sopenharmony_ci val = YT8531_SCR_SYNCE_ENABLE | 109362306a36Sopenharmony_ci FIELD_PREP(YT8531_SCR_CLK_SRC_MASK, 109462306a36Sopenharmony_ci YT8531_SCR_CLK_SRC_REF_25M); 109562306a36Sopenharmony_ci break; 109662306a36Sopenharmony_ci case YTPHY_DTS_OUTPUT_CLK_125M: 109762306a36Sopenharmony_ci mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK | 109862306a36Sopenharmony_ci YT8531_SCR_CLK_FRE_SEL_125M; 109962306a36Sopenharmony_ci val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M | 110062306a36Sopenharmony_ci FIELD_PREP(YT8531_SCR_CLK_SRC_MASK, 110162306a36Sopenharmony_ci YT8531_SCR_CLK_SRC_PLL_125M); 110262306a36Sopenharmony_ci break; 110362306a36Sopenharmony_ci default: 110462306a36Sopenharmony_ci phydev_warn(phydev, "Freq err:%u\n", freq); 110562306a36Sopenharmony_ci return -EINVAL; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask, 110962306a36Sopenharmony_ci val); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/** 111362306a36Sopenharmony_ci * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp 111462306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 111562306a36Sopenharmony_ci * 111662306a36Sopenharmony_ci * NOTE:The caller must have taken the MDIO bus lock. 111762306a36Sopenharmony_ci * 111862306a36Sopenharmony_ci * returns 0 or negative errno code 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_cistatic int ytphy_utp_read_lpa(struct phy_device *phydev) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci int lpa, lpagb; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE) { 112562306a36Sopenharmony_ci if (!phydev->autoneg_complete) { 112662306a36Sopenharmony_ci mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 112762306a36Sopenharmony_ci 0); 112862306a36Sopenharmony_ci mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0); 112962306a36Sopenharmony_ci return 0; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (phydev->is_gigabit_capable) { 113362306a36Sopenharmony_ci lpagb = __phy_read(phydev, MII_STAT1000); 113462306a36Sopenharmony_ci if (lpagb < 0) 113562306a36Sopenharmony_ci return lpagb; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (lpagb & LPA_1000MSFAIL) { 113862306a36Sopenharmony_ci int adv = __phy_read(phydev, MII_CTRL1000); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (adv < 0) 114162306a36Sopenharmony_ci return adv; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (adv & CTL1000_ENABLE_MASTER) 114462306a36Sopenharmony_ci phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); 114562306a36Sopenharmony_ci else 114662306a36Sopenharmony_ci phydev_err(phydev, "Master/Slave resolution failed\n"); 114762306a36Sopenharmony_ci return -ENOLINK; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 115162306a36Sopenharmony_ci lpagb); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci lpa = __phy_read(phydev, MII_LPA); 115562306a36Sopenharmony_ci if (lpa < 0) 115662306a36Sopenharmony_ci return lpa; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); 115962306a36Sopenharmony_ci } else { 116062306a36Sopenharmony_ci linkmode_zero(phydev->lp_advertising); 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci return 0; 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci/** 116762306a36Sopenharmony_ci * yt8521_adjust_status() - update speed and duplex to phydev. when in fiber 116862306a36Sopenharmony_ci * mode, adjust speed and duplex. 116962306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 117062306a36Sopenharmony_ci * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG 117162306a36Sopenharmony_ci * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode) 117262306a36Sopenharmony_ci * 117362306a36Sopenharmony_ci * NOTE:The caller must have taken the MDIO bus lock. 117462306a36Sopenharmony_ci * 117562306a36Sopenharmony_ci * returns 0 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_cistatic int yt8521_adjust_status(struct phy_device *phydev, int status, 117862306a36Sopenharmony_ci bool is_utp) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci int speed_mode, duplex; 118162306a36Sopenharmony_ci int speed; 118262306a36Sopenharmony_ci int err; 118362306a36Sopenharmony_ci int lpa; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (is_utp) 118662306a36Sopenharmony_ci duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET; 118762306a36Sopenharmony_ci else 118862306a36Sopenharmony_ci duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */ 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >> 119162306a36Sopenharmony_ci YTPHY_SSR_SPEED_MODE_OFFSET; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci switch (speed_mode) { 119462306a36Sopenharmony_ci case YTPHY_SSR_SPEED_10M: 119562306a36Sopenharmony_ci if (is_utp) 119662306a36Sopenharmony_ci speed = SPEED_10; 119762306a36Sopenharmony_ci else 119862306a36Sopenharmony_ci /* for fiber, it will never run here, default to 119962306a36Sopenharmony_ci * SPEED_UNKNOWN 120062306a36Sopenharmony_ci */ 120162306a36Sopenharmony_ci speed = SPEED_UNKNOWN; 120262306a36Sopenharmony_ci break; 120362306a36Sopenharmony_ci case YTPHY_SSR_SPEED_100M: 120462306a36Sopenharmony_ci speed = SPEED_100; 120562306a36Sopenharmony_ci break; 120662306a36Sopenharmony_ci case YTPHY_SSR_SPEED_1000M: 120762306a36Sopenharmony_ci speed = SPEED_1000; 120862306a36Sopenharmony_ci break; 120962306a36Sopenharmony_ci default: 121062306a36Sopenharmony_ci speed = SPEED_UNKNOWN; 121162306a36Sopenharmony_ci break; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci phydev->speed = speed; 121562306a36Sopenharmony_ci phydev->duplex = duplex; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (is_utp) { 121862306a36Sopenharmony_ci err = ytphy_utp_read_lpa(phydev); 121962306a36Sopenharmony_ci if (err < 0) 122062306a36Sopenharmony_ci return err; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci phy_resolve_aneg_pause(phydev); 122362306a36Sopenharmony_ci } else { 122462306a36Sopenharmony_ci lpa = __phy_read(phydev, MII_LPA); 122562306a36Sopenharmony_ci if (lpa < 0) 122662306a36Sopenharmony_ci return lpa; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* only support 1000baseX Full */ 122962306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 123062306a36Sopenharmony_ci phydev->lp_advertising, lpa & LPA_1000XFULL); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (!(lpa & YTPHY_FLPA_PAUSE)) { 123362306a36Sopenharmony_ci phydev->pause = 0; 123462306a36Sopenharmony_ci phydev->asym_pause = 0; 123562306a36Sopenharmony_ci } else if ((lpa & YTPHY_FLPA_ASYM_PAUSE)) { 123662306a36Sopenharmony_ci phydev->pause = 1; 123762306a36Sopenharmony_ci phydev->asym_pause = 1; 123862306a36Sopenharmony_ci } else { 123962306a36Sopenharmony_ci phydev->pause = 1; 124062306a36Sopenharmony_ci phydev->asym_pause = 0; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci return 0; 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci/** 124862306a36Sopenharmony_ci * yt8521_read_status_paged() - determines the speed and duplex of one page 124962306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 125062306a36Sopenharmony_ci * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to 125162306a36Sopenharmony_ci * operate. 125262306a36Sopenharmony_ci * 125362306a36Sopenharmony_ci * returns 1 (utp or fiber link),0 (no link) or negative errno code 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_cistatic int yt8521_read_status_paged(struct phy_device *phydev, int page) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci int fiber_latch_val; 125862306a36Sopenharmony_ci int fiber_curr_val; 125962306a36Sopenharmony_ci int old_page; 126062306a36Sopenharmony_ci int ret = 0; 126162306a36Sopenharmony_ci int status; 126262306a36Sopenharmony_ci int link; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci linkmode_zero(phydev->lp_advertising); 126562306a36Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 126662306a36Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 126762306a36Sopenharmony_ci phydev->asym_pause = 0; 126862306a36Sopenharmony_ci phydev->pause = 0; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* YT8521 has two reg space (utp/fiber) for linkup with utp/fiber 127162306a36Sopenharmony_ci * respectively. but for utp/fiber combo mode, reg space should be 127262306a36Sopenharmony_ci * arbitrated based on media priority. by default, utp takes 127362306a36Sopenharmony_ci * priority. reg space should be properly set before read 127462306a36Sopenharmony_ci * YTPHY_SPECIFIC_STATUS_REG. 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci page &= YT8521_RSSR_SPACE_MASK; 127862306a36Sopenharmony_ci old_page = phy_select_page(phydev, page); 127962306a36Sopenharmony_ci if (old_page < 0) 128062306a36Sopenharmony_ci goto err_restore_page; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci /* Read YTPHY_SPECIFIC_STATUS_REG, which indicates the speed and duplex 128362306a36Sopenharmony_ci * of the PHY is actually using. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_ci ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); 128662306a36Sopenharmony_ci if (ret < 0) 128762306a36Sopenharmony_ci goto err_restore_page; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci status = ret; 129062306a36Sopenharmony_ci link = !!(status & YTPHY_SSR_LINK); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci /* When PHY is in fiber mode, speed transferred from 1000Mbps to 129362306a36Sopenharmony_ci * 100Mbps,there is not link down from YTPHY_SPECIFIC_STATUS_REG, so 129462306a36Sopenharmony_ci * we need check MII_BMSR to identify such case. 129562306a36Sopenharmony_ci */ 129662306a36Sopenharmony_ci if (page == YT8521_RSSR_FIBER_SPACE) { 129762306a36Sopenharmony_ci ret = __phy_read(phydev, MII_BMSR); 129862306a36Sopenharmony_ci if (ret < 0) 129962306a36Sopenharmony_ci goto err_restore_page; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci fiber_latch_val = ret; 130262306a36Sopenharmony_ci ret = __phy_read(phydev, MII_BMSR); 130362306a36Sopenharmony_ci if (ret < 0) 130462306a36Sopenharmony_ci goto err_restore_page; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci fiber_curr_val = ret; 130762306a36Sopenharmony_ci if (link && fiber_latch_val != fiber_curr_val) { 130862306a36Sopenharmony_ci link = 0; 130962306a36Sopenharmony_ci phydev_info(phydev, 131062306a36Sopenharmony_ci "%s, fiber link down detect, latch = %04x, curr = %04x\n", 131162306a36Sopenharmony_ci __func__, fiber_latch_val, fiber_curr_val); 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci } else { 131462306a36Sopenharmony_ci /* Read autonegotiation status */ 131562306a36Sopenharmony_ci ret = __phy_read(phydev, MII_BMSR); 131662306a36Sopenharmony_ci if (ret < 0) 131762306a36Sopenharmony_ci goto err_restore_page; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci phydev->autoneg_complete = ret & BMSR_ANEGCOMPLETE ? 1 : 0; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (link) { 132362306a36Sopenharmony_ci if (page == YT8521_RSSR_UTP_SPACE) 132462306a36Sopenharmony_ci yt8521_adjust_status(phydev, status, true); 132562306a36Sopenharmony_ci else 132662306a36Sopenharmony_ci yt8521_adjust_status(phydev, status, false); 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, link); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cierr_restore_page: 133162306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, ret); 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci/** 133562306a36Sopenharmony_ci * yt8521_read_status() - determines the negotiated speed and duplex 133662306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 133762306a36Sopenharmony_ci * 133862306a36Sopenharmony_ci * returns 0 or negative errno code 133962306a36Sopenharmony_ci */ 134062306a36Sopenharmony_cistatic int yt8521_read_status(struct phy_device *phydev) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci struct yt8521_priv *priv = phydev->priv; 134362306a36Sopenharmony_ci int link_fiber = 0; 134462306a36Sopenharmony_ci int link_utp; 134562306a36Sopenharmony_ci int link; 134662306a36Sopenharmony_ci int ret; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { 134962306a36Sopenharmony_ci link = yt8521_read_status_paged(phydev, priv->reg_page); 135062306a36Sopenharmony_ci if (link < 0) 135162306a36Sopenharmony_ci return link; 135262306a36Sopenharmony_ci } else { 135362306a36Sopenharmony_ci /* when page is YT8521_RSSR_TO_BE_ARBITRATED, arbitration is 135462306a36Sopenharmony_ci * needed. by default, utp is higher priority. 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci link_utp = yt8521_read_status_paged(phydev, 135862306a36Sopenharmony_ci YT8521_RSSR_UTP_SPACE); 135962306a36Sopenharmony_ci if (link_utp < 0) 136062306a36Sopenharmony_ci return link_utp; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (!link_utp) { 136362306a36Sopenharmony_ci link_fiber = yt8521_read_status_paged(phydev, 136462306a36Sopenharmony_ci YT8521_RSSR_FIBER_SPACE); 136562306a36Sopenharmony_ci if (link_fiber < 0) 136662306a36Sopenharmony_ci return link_fiber; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci link = link_utp || link_fiber; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci if (link) { 137362306a36Sopenharmony_ci if (phydev->link == 0) { 137462306a36Sopenharmony_ci /* arbitrate reg space based on linkup media type. */ 137562306a36Sopenharmony_ci if (priv->polling_mode == YT8521_MODE_POLL && 137662306a36Sopenharmony_ci priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) { 137762306a36Sopenharmony_ci if (link_fiber) 137862306a36Sopenharmony_ci priv->reg_page = 137962306a36Sopenharmony_ci YT8521_RSSR_FIBER_SPACE; 138062306a36Sopenharmony_ci else 138162306a36Sopenharmony_ci priv->reg_page = YT8521_RSSR_UTP_SPACE; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci ret = ytphy_write_ext_with_lock(phydev, 138462306a36Sopenharmony_ci YT8521_REG_SPACE_SELECT_REG, 138562306a36Sopenharmony_ci priv->reg_page); 138662306a36Sopenharmony_ci if (ret < 0) 138762306a36Sopenharmony_ci return ret; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci phydev->port = link_fiber ? PORT_FIBRE : PORT_TP; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci phydev_info(phydev, "%s, link up, media: %s\n", 139262306a36Sopenharmony_ci __func__, 139362306a36Sopenharmony_ci (phydev->port == PORT_TP) ? 139462306a36Sopenharmony_ci "UTP" : "Fiber"); 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci phydev->link = 1; 139862306a36Sopenharmony_ci } else { 139962306a36Sopenharmony_ci if (phydev->link == 1) { 140062306a36Sopenharmony_ci phydev_info(phydev, "%s, link down, media: %s\n", 140162306a36Sopenharmony_ci __func__, (phydev->port == PORT_TP) ? 140262306a36Sopenharmony_ci "UTP" : "Fiber"); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci /* When in YT8521_MODE_POLL mode, need prepare for next 140562306a36Sopenharmony_ci * arbitration. 140662306a36Sopenharmony_ci */ 140762306a36Sopenharmony_ci if (priv->polling_mode == YT8521_MODE_POLL) { 140862306a36Sopenharmony_ci priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED; 140962306a36Sopenharmony_ci phydev->port = PORT_NONE; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci phydev->link = 0; 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci return 0; 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci/** 142062306a36Sopenharmony_ci * yt8521_modify_bmcr_paged - bits modify a PHY's BMCR register of one page 142162306a36Sopenharmony_ci * @phydev: the phy_device struct 142262306a36Sopenharmony_ci * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to operate 142362306a36Sopenharmony_ci * @mask: bit mask of bits to clear 142462306a36Sopenharmony_ci * @set: bit mask of bits to set 142562306a36Sopenharmony_ci * 142662306a36Sopenharmony_ci * NOTE: Convenience function which allows a PHY's BMCR register to be 142762306a36Sopenharmony_ci * modified as new register value = (old register value & ~mask) | set. 142862306a36Sopenharmony_ci * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space 142962306a36Sopenharmony_ci * has MII_BMCR. poll mode combines utp and faber,so need do both. 143062306a36Sopenharmony_ci * If it is reset, it will wait for completion. 143162306a36Sopenharmony_ci * 143262306a36Sopenharmony_ci * returns 0 or negative errno code 143362306a36Sopenharmony_ci */ 143462306a36Sopenharmony_cistatic int yt8521_modify_bmcr_paged(struct phy_device *phydev, int page, 143562306a36Sopenharmony_ci u16 mask, u16 set) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci int max_cnt = 500; /* the max wait time of reset ~ 500 ms */ 143862306a36Sopenharmony_ci int old_page; 143962306a36Sopenharmony_ci int ret = 0; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); 144262306a36Sopenharmony_ci if (old_page < 0) 144362306a36Sopenharmony_ci goto err_restore_page; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci ret = __phy_modify(phydev, MII_BMCR, mask, set); 144662306a36Sopenharmony_ci if (ret < 0) 144762306a36Sopenharmony_ci goto err_restore_page; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci /* If it is reset, need to wait for the reset to complete */ 145062306a36Sopenharmony_ci if (set == BMCR_RESET) { 145162306a36Sopenharmony_ci while (max_cnt--) { 145262306a36Sopenharmony_ci usleep_range(1000, 1100); 145362306a36Sopenharmony_ci ret = __phy_read(phydev, MII_BMCR); 145462306a36Sopenharmony_ci if (ret < 0) 145562306a36Sopenharmony_ci goto err_restore_page; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci if (!(ret & BMCR_RESET)) 145862306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, 0); 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_cierr_restore_page: 146362306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, ret); 146462306a36Sopenharmony_ci} 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci/** 146762306a36Sopenharmony_ci * yt8521_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register 146862306a36Sopenharmony_ci * @phydev: the phy_device struct 146962306a36Sopenharmony_ci * @mask: bit mask of bits to clear 147062306a36Sopenharmony_ci * @set: bit mask of bits to set 147162306a36Sopenharmony_ci * 147262306a36Sopenharmony_ci * NOTE: Convenience function which allows a PHY's BMCR register to be 147362306a36Sopenharmony_ci * modified as new register value = (old register value & ~mask) | set. 147462306a36Sopenharmony_ci * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space 147562306a36Sopenharmony_ci * has MII_BMCR. poll mode combines utp and faber,so need do both. 147662306a36Sopenharmony_ci * 147762306a36Sopenharmony_ci * returns 0 or negative errno code 147862306a36Sopenharmony_ci */ 147962306a36Sopenharmony_cistatic int yt8521_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask, 148062306a36Sopenharmony_ci u16 set) 148162306a36Sopenharmony_ci{ 148262306a36Sopenharmony_ci struct yt8521_priv *priv = phydev->priv; 148362306a36Sopenharmony_ci int ret; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { 148662306a36Sopenharmony_ci ret = yt8521_modify_bmcr_paged(phydev, priv->reg_page, mask, 148762306a36Sopenharmony_ci set); 148862306a36Sopenharmony_ci if (ret < 0) 148962306a36Sopenharmony_ci return ret; 149062306a36Sopenharmony_ci } else { 149162306a36Sopenharmony_ci ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE, 149262306a36Sopenharmony_ci mask, set); 149362306a36Sopenharmony_ci if (ret < 0) 149462306a36Sopenharmony_ci return ret; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE, 149762306a36Sopenharmony_ci mask, set); 149862306a36Sopenharmony_ci if (ret < 0) 149962306a36Sopenharmony_ci return ret; 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci return 0; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci/** 150562306a36Sopenharmony_ci * yt8521_soft_reset() - called to issue a PHY software reset 150662306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 150762306a36Sopenharmony_ci * 150862306a36Sopenharmony_ci * returns 0 or negative errno code 150962306a36Sopenharmony_ci */ 151062306a36Sopenharmony_cistatic int yt8521_soft_reset(struct phy_device *phydev) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_RESET); 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci/** 151662306a36Sopenharmony_ci * yt8521_suspend() - suspend the hardware 151762306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 151862306a36Sopenharmony_ci * 151962306a36Sopenharmony_ci * returns 0 or negative errno code 152062306a36Sopenharmony_ci */ 152162306a36Sopenharmony_cistatic int yt8521_suspend(struct phy_device *phydev) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci int wol_config; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci /* YTPHY_WOL_CONFIG_REG is common ext reg */ 152662306a36Sopenharmony_ci wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); 152762306a36Sopenharmony_ci if (wol_config < 0) 152862306a36Sopenharmony_ci return wol_config; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci /* if wol enable, do nothing */ 153162306a36Sopenharmony_ci if (wol_config & YTPHY_WCR_ENABLE) 153262306a36Sopenharmony_ci return 0; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN); 153562306a36Sopenharmony_ci} 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci/** 153862306a36Sopenharmony_ci * yt8521_resume() - resume the hardware 153962306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 154062306a36Sopenharmony_ci * 154162306a36Sopenharmony_ci * returns 0 or negative errno code 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_cistatic int yt8521_resume(struct phy_device *phydev) 154462306a36Sopenharmony_ci{ 154562306a36Sopenharmony_ci int ret; 154662306a36Sopenharmony_ci int wol_config; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci /* disable auto sleep */ 154962306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, 155062306a36Sopenharmony_ci YT8521_EXTREG_SLEEP_CONTROL1_REG, 155162306a36Sopenharmony_ci YT8521_ESC1R_SLEEP_SW, 0); 155262306a36Sopenharmony_ci if (ret < 0) 155362306a36Sopenharmony_ci return ret; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); 155662306a36Sopenharmony_ci if (wol_config < 0) 155762306a36Sopenharmony_ci return wol_config; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci /* if wol enable, do nothing */ 156062306a36Sopenharmony_ci if (wol_config & YTPHY_WCR_ENABLE) 156162306a36Sopenharmony_ci return 0; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0); 156462306a36Sopenharmony_ci} 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci/** 156762306a36Sopenharmony_ci * yt8521_config_init() - called to initialize the PHY 156862306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 156962306a36Sopenharmony_ci * 157062306a36Sopenharmony_ci * returns 0 or negative errno code 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_cistatic int yt8521_config_init(struct phy_device *phydev) 157362306a36Sopenharmony_ci{ 157462306a36Sopenharmony_ci struct device_node *node = phydev->mdio.dev.of_node; 157562306a36Sopenharmony_ci int old_page; 157662306a36Sopenharmony_ci int ret = 0; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); 157962306a36Sopenharmony_ci if (old_page < 0) 158062306a36Sopenharmony_ci goto err_restore_page; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci /* set rgmii delay mode */ 158362306a36Sopenharmony_ci if (phydev->interface != PHY_INTERFACE_MODE_SGMII) { 158462306a36Sopenharmony_ci ret = ytphy_rgmii_clk_delay_config(phydev); 158562306a36Sopenharmony_ci if (ret < 0) 158662306a36Sopenharmony_ci goto err_restore_page; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) { 159062306a36Sopenharmony_ci /* disable auto sleep */ 159162306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, 159262306a36Sopenharmony_ci YT8521_ESC1R_SLEEP_SW, 0); 159362306a36Sopenharmony_ci if (ret < 0) 159462306a36Sopenharmony_ci goto err_restore_page; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) { 159862306a36Sopenharmony_ci /* enable RXC clock when no wire plug */ 159962306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG, 160062306a36Sopenharmony_ci YT8521_CGR_RX_CLK_EN, 0); 160162306a36Sopenharmony_ci if (ret < 0) 160262306a36Sopenharmony_ci goto err_restore_page; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_cierr_restore_page: 160562306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, ret); 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic int yt8531_config_init(struct phy_device *phydev) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci struct device_node *node = phydev->mdio.dev.of_node; 161162306a36Sopenharmony_ci int ret; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci ret = ytphy_rgmii_clk_delay_config_with_lock(phydev); 161462306a36Sopenharmony_ci if (ret < 0) 161562306a36Sopenharmony_ci return ret; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) { 161862306a36Sopenharmony_ci /* disable auto sleep */ 161962306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, 162062306a36Sopenharmony_ci YT8521_EXTREG_SLEEP_CONTROL1_REG, 162162306a36Sopenharmony_ci YT8521_ESC1R_SLEEP_SW, 0); 162262306a36Sopenharmony_ci if (ret < 0) 162362306a36Sopenharmony_ci return ret; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) { 162762306a36Sopenharmony_ci /* enable RXC clock when no wire plug */ 162862306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, 162962306a36Sopenharmony_ci YT8521_CLOCK_GATING_REG, 163062306a36Sopenharmony_ci YT8521_CGR_RX_CLK_EN, 0); 163162306a36Sopenharmony_ci if (ret < 0) 163262306a36Sopenharmony_ci return ret; 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci ret = yt8531_set_ds(phydev); 163662306a36Sopenharmony_ci if (ret < 0) 163762306a36Sopenharmony_ci return ret; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return 0; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci/** 164362306a36Sopenharmony_ci * yt8531_link_change_notify() - Adjust the tx clock direction according to 164462306a36Sopenharmony_ci * the current speed and dts config. 164562306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 164662306a36Sopenharmony_ci * 164762306a36Sopenharmony_ci * NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please 164862306a36Sopenharmony_ci * keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not 164962306a36Sopenharmony_ci * JH7110. 165062306a36Sopenharmony_ci */ 165162306a36Sopenharmony_cistatic void yt8531_link_change_notify(struct phy_device *phydev) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci struct device_node *node = phydev->mdio.dev.of_node; 165462306a36Sopenharmony_ci bool tx_clk_1000_inverted = false; 165562306a36Sopenharmony_ci bool tx_clk_100_inverted = false; 165662306a36Sopenharmony_ci bool tx_clk_10_inverted = false; 165762306a36Sopenharmony_ci bool tx_clk_adj_enabled = false; 165862306a36Sopenharmony_ci u16 val = 0; 165962306a36Sopenharmony_ci int ret; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled")) 166262306a36Sopenharmony_ci tx_clk_adj_enabled = true; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci if (!tx_clk_adj_enabled) 166562306a36Sopenharmony_ci return; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted")) 166862306a36Sopenharmony_ci tx_clk_10_inverted = true; 166962306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted")) 167062306a36Sopenharmony_ci tx_clk_100_inverted = true; 167162306a36Sopenharmony_ci if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted")) 167262306a36Sopenharmony_ci tx_clk_1000_inverted = true; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci if (phydev->speed < 0) 167562306a36Sopenharmony_ci return; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci switch (phydev->speed) { 167862306a36Sopenharmony_ci case SPEED_1000: 167962306a36Sopenharmony_ci if (tx_clk_1000_inverted) 168062306a36Sopenharmony_ci val = YT8521_RC1R_TX_CLK_SEL_INVERTED; 168162306a36Sopenharmony_ci break; 168262306a36Sopenharmony_ci case SPEED_100: 168362306a36Sopenharmony_ci if (tx_clk_100_inverted) 168462306a36Sopenharmony_ci val = YT8521_RC1R_TX_CLK_SEL_INVERTED; 168562306a36Sopenharmony_ci break; 168662306a36Sopenharmony_ci case SPEED_10: 168762306a36Sopenharmony_ci if (tx_clk_10_inverted) 168862306a36Sopenharmony_ci val = YT8521_RC1R_TX_CLK_SEL_INVERTED; 168962306a36Sopenharmony_ci break; 169062306a36Sopenharmony_ci default: 169162306a36Sopenharmony_ci return; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG, 169562306a36Sopenharmony_ci YT8521_RC1R_TX_CLK_SEL_INVERTED, val); 169662306a36Sopenharmony_ci if (ret < 0) 169762306a36Sopenharmony_ci phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret); 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci/** 170162306a36Sopenharmony_ci * yt8521_prepare_fiber_features() - A small helper function that setup 170262306a36Sopenharmony_ci * fiber's features. 170362306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 170462306a36Sopenharmony_ci * @dst: a pointer to store fiber's features 170562306a36Sopenharmony_ci */ 170662306a36Sopenharmony_cistatic void yt8521_prepare_fiber_features(struct phy_device *phydev, 170762306a36Sopenharmony_ci unsigned long *dst) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, dst); 171062306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, dst); 171162306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, dst); 171262306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, dst); 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci/** 171662306a36Sopenharmony_ci * yt8521_fiber_setup_forced - configures/forces speed from @phydev 171762306a36Sopenharmony_ci * @phydev: target phy_device struct 171862306a36Sopenharmony_ci * 171962306a36Sopenharmony_ci * NOTE:The caller must have taken the MDIO bus lock. 172062306a36Sopenharmony_ci * 172162306a36Sopenharmony_ci * returns 0 or negative errno code 172262306a36Sopenharmony_ci */ 172362306a36Sopenharmony_cistatic int yt8521_fiber_setup_forced(struct phy_device *phydev) 172462306a36Sopenharmony_ci{ 172562306a36Sopenharmony_ci u16 val; 172662306a36Sopenharmony_ci int ret; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci if (phydev->speed == SPEED_1000) 172962306a36Sopenharmony_ci val = YTPHY_MCR_FIBER_1000BX; 173062306a36Sopenharmony_ci else if (phydev->speed == SPEED_100) 173162306a36Sopenharmony_ci val = YTPHY_MCR_FIBER_100FX; 173262306a36Sopenharmony_ci else 173362306a36Sopenharmony_ci return -EINVAL; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); 173662306a36Sopenharmony_ci if (ret < 0) 173762306a36Sopenharmony_ci return ret; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci /* disable Fiber auto sensing */ 174062306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG, 174162306a36Sopenharmony_ci YT8521_LTCR_EN_AUTOSEN, 0); 174262306a36Sopenharmony_ci if (ret < 0) 174362306a36Sopenharmony_ci return ret; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci ret = ytphy_modify_ext(phydev, YTPHY_MISC_CONFIG_REG, 174662306a36Sopenharmony_ci YTPHY_MCR_FIBER_SPEED_MASK, val); 174762306a36Sopenharmony_ci if (ret < 0) 174862306a36Sopenharmony_ci return ret; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, 175162306a36Sopenharmony_ci YT8521_CCR_SW_RST, 0); 175262306a36Sopenharmony_ci} 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci/** 175562306a36Sopenharmony_ci * ytphy_check_and_restart_aneg - Enable and restart auto-negotiation 175662306a36Sopenharmony_ci * @phydev: target phy_device struct 175762306a36Sopenharmony_ci * @restart: whether aneg restart is requested 175862306a36Sopenharmony_ci * 175962306a36Sopenharmony_ci * NOTE:The caller must have taken the MDIO bus lock. 176062306a36Sopenharmony_ci * 176162306a36Sopenharmony_ci * returns 0 or negative errno code 176262306a36Sopenharmony_ci */ 176362306a36Sopenharmony_cistatic int ytphy_check_and_restart_aneg(struct phy_device *phydev, bool restart) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci int ret; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (!restart) { 176862306a36Sopenharmony_ci /* Advertisement hasn't changed, but maybe aneg was never on to 176962306a36Sopenharmony_ci * begin with? Or maybe phy was isolated? 177062306a36Sopenharmony_ci */ 177162306a36Sopenharmony_ci ret = __phy_read(phydev, MII_BMCR); 177262306a36Sopenharmony_ci if (ret < 0) 177362306a36Sopenharmony_ci return ret; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE)) 177662306a36Sopenharmony_ci restart = true; 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci /* Enable and Restart Autonegotiation 177962306a36Sopenharmony_ci * Don't isolate the PHY if we're negotiating 178062306a36Sopenharmony_ci */ 178162306a36Sopenharmony_ci if (restart) 178262306a36Sopenharmony_ci return __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, 178362306a36Sopenharmony_ci BMCR_ANENABLE | BMCR_ANRESTART); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci return 0; 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci/** 178962306a36Sopenharmony_ci * yt8521_fiber_config_aneg - restart auto-negotiation or write 179062306a36Sopenharmony_ci * YTPHY_MISC_CONFIG_REG. 179162306a36Sopenharmony_ci * @phydev: target phy_device struct 179262306a36Sopenharmony_ci * 179362306a36Sopenharmony_ci * NOTE:The caller must have taken the MDIO bus lock. 179462306a36Sopenharmony_ci * 179562306a36Sopenharmony_ci * returns 0 or negative errno code 179662306a36Sopenharmony_ci */ 179762306a36Sopenharmony_cistatic int yt8521_fiber_config_aneg(struct phy_device *phydev) 179862306a36Sopenharmony_ci{ 179962306a36Sopenharmony_ci int err, changed = 0; 180062306a36Sopenharmony_ci int bmcr; 180162306a36Sopenharmony_ci u16 adv; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE) 180462306a36Sopenharmony_ci return yt8521_fiber_setup_forced(phydev); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci /* enable Fiber auto sensing */ 180762306a36Sopenharmony_ci err = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG, 180862306a36Sopenharmony_ci 0, YT8521_LTCR_EN_AUTOSEN); 180962306a36Sopenharmony_ci if (err < 0) 181062306a36Sopenharmony_ci return err; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci err = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, 181362306a36Sopenharmony_ci YT8521_CCR_SW_RST, 0); 181462306a36Sopenharmony_ci if (err < 0) 181562306a36Sopenharmony_ci return err; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci bmcr = __phy_read(phydev, MII_BMCR); 181862306a36Sopenharmony_ci if (bmcr < 0) 181962306a36Sopenharmony_ci return bmcr; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci /* When it is coming from fiber forced mode, add bmcr power down 182262306a36Sopenharmony_ci * and power up to let aneg work fine. 182362306a36Sopenharmony_ci */ 182462306a36Sopenharmony_ci if (!(bmcr & BMCR_ANENABLE)) { 182562306a36Sopenharmony_ci __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN); 182662306a36Sopenharmony_ci usleep_range(1000, 1100); 182762306a36Sopenharmony_ci __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0); 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci adv = linkmode_adv_to_mii_adv_x(phydev->advertising, 183162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseX_Full_BIT); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* Setup fiber advertisement */ 183462306a36Sopenharmony_ci err = __phy_modify_changed(phydev, MII_ADVERTISE, 183562306a36Sopenharmony_ci ADVERTISE_1000XHALF | ADVERTISE_1000XFULL | 183662306a36Sopenharmony_ci ADVERTISE_1000XPAUSE | 183762306a36Sopenharmony_ci ADVERTISE_1000XPSE_ASYM, 183862306a36Sopenharmony_ci adv); 183962306a36Sopenharmony_ci if (err < 0) 184062306a36Sopenharmony_ci return err; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci if (err > 0) 184362306a36Sopenharmony_ci changed = 1; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci return ytphy_check_and_restart_aneg(phydev, changed); 184662306a36Sopenharmony_ci} 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci/** 184962306a36Sopenharmony_ci * ytphy_setup_master_slave 185062306a36Sopenharmony_ci * @phydev: target phy_device struct 185162306a36Sopenharmony_ci * 185262306a36Sopenharmony_ci * NOTE: The caller must have taken the MDIO bus lock. 185362306a36Sopenharmony_ci * 185462306a36Sopenharmony_ci * returns 0 or negative errno code 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_cistatic int ytphy_setup_master_slave(struct phy_device *phydev) 185762306a36Sopenharmony_ci{ 185862306a36Sopenharmony_ci u16 ctl = 0; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci if (!phydev->is_gigabit_capable) 186162306a36Sopenharmony_ci return 0; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci switch (phydev->master_slave_set) { 186462306a36Sopenharmony_ci case MASTER_SLAVE_CFG_MASTER_PREFERRED: 186562306a36Sopenharmony_ci ctl |= CTL1000_PREFER_MASTER; 186662306a36Sopenharmony_ci break; 186762306a36Sopenharmony_ci case MASTER_SLAVE_CFG_SLAVE_PREFERRED: 186862306a36Sopenharmony_ci break; 186962306a36Sopenharmony_ci case MASTER_SLAVE_CFG_MASTER_FORCE: 187062306a36Sopenharmony_ci ctl |= CTL1000_AS_MASTER; 187162306a36Sopenharmony_ci fallthrough; 187262306a36Sopenharmony_ci case MASTER_SLAVE_CFG_SLAVE_FORCE: 187362306a36Sopenharmony_ci ctl |= CTL1000_ENABLE_MASTER; 187462306a36Sopenharmony_ci break; 187562306a36Sopenharmony_ci case MASTER_SLAVE_CFG_UNKNOWN: 187662306a36Sopenharmony_ci case MASTER_SLAVE_CFG_UNSUPPORTED: 187762306a36Sopenharmony_ci return 0; 187862306a36Sopenharmony_ci default: 187962306a36Sopenharmony_ci phydev_warn(phydev, "Unsupported Master/Slave mode\n"); 188062306a36Sopenharmony_ci return -EOPNOTSUPP; 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci return __phy_modify_changed(phydev, MII_CTRL1000, 188462306a36Sopenharmony_ci (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER | 188562306a36Sopenharmony_ci CTL1000_PREFER_MASTER), ctl); 188662306a36Sopenharmony_ci} 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci/** 188962306a36Sopenharmony_ci * ytphy_utp_config_advert - sanitize and advertise auto-negotiation parameters 189062306a36Sopenharmony_ci * @phydev: target phy_device struct 189162306a36Sopenharmony_ci * 189262306a36Sopenharmony_ci * NOTE: Writes MII_ADVERTISE with the appropriate values, 189362306a36Sopenharmony_ci * after sanitizing the values to make sure we only advertise 189462306a36Sopenharmony_ci * what is supported. Returns < 0 on error, 0 if the PHY's advertisement 189562306a36Sopenharmony_ci * hasn't changed, and > 0 if it has changed. 189662306a36Sopenharmony_ci * The caller must have taken the MDIO bus lock. 189762306a36Sopenharmony_ci * 189862306a36Sopenharmony_ci * returns 0 or negative errno code 189962306a36Sopenharmony_ci */ 190062306a36Sopenharmony_cistatic int ytphy_utp_config_advert(struct phy_device *phydev) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci int err, bmsr, changed = 0; 190362306a36Sopenharmony_ci u32 adv; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci /* Only allow advertising what this PHY supports */ 190662306a36Sopenharmony_ci linkmode_and(phydev->advertising, phydev->advertising, 190762306a36Sopenharmony_ci phydev->supported); 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci adv = linkmode_adv_to_mii_adv_t(phydev->advertising); 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci /* Setup standard advertisement */ 191262306a36Sopenharmony_ci err = __phy_modify_changed(phydev, MII_ADVERTISE, 191362306a36Sopenharmony_ci ADVERTISE_ALL | ADVERTISE_100BASE4 | 191462306a36Sopenharmony_ci ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, 191562306a36Sopenharmony_ci adv); 191662306a36Sopenharmony_ci if (err < 0) 191762306a36Sopenharmony_ci return err; 191862306a36Sopenharmony_ci if (err > 0) 191962306a36Sopenharmony_ci changed = 1; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci bmsr = __phy_read(phydev, MII_BMSR); 192262306a36Sopenharmony_ci if (bmsr < 0) 192362306a36Sopenharmony_ci return bmsr; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all 192662306a36Sopenharmony_ci * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a 192762306a36Sopenharmony_ci * logical 1. 192862306a36Sopenharmony_ci */ 192962306a36Sopenharmony_ci if (!(bmsr & BMSR_ESTATEN)) 193062306a36Sopenharmony_ci return changed; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci err = __phy_modify_changed(phydev, MII_CTRL1000, 193562306a36Sopenharmony_ci ADVERTISE_1000FULL | ADVERTISE_1000HALF, 193662306a36Sopenharmony_ci adv); 193762306a36Sopenharmony_ci if (err < 0) 193862306a36Sopenharmony_ci return err; 193962306a36Sopenharmony_ci if (err > 0) 194062306a36Sopenharmony_ci changed = 1; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci return changed; 194362306a36Sopenharmony_ci} 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci/** 194662306a36Sopenharmony_ci * ytphy_utp_config_aneg - restart auto-negotiation or write BMCR 194762306a36Sopenharmony_ci * @phydev: target phy_device struct 194862306a36Sopenharmony_ci * @changed: whether autoneg is requested 194962306a36Sopenharmony_ci * 195062306a36Sopenharmony_ci * NOTE: If auto-negotiation is enabled, we configure the 195162306a36Sopenharmony_ci * advertising, and then restart auto-negotiation. If it is not 195262306a36Sopenharmony_ci * enabled, then we write the BMCR. 195362306a36Sopenharmony_ci * The caller must have taken the MDIO bus lock. 195462306a36Sopenharmony_ci * 195562306a36Sopenharmony_ci * returns 0 or negative errno code 195662306a36Sopenharmony_ci */ 195762306a36Sopenharmony_cistatic int ytphy_utp_config_aneg(struct phy_device *phydev, bool changed) 195862306a36Sopenharmony_ci{ 195962306a36Sopenharmony_ci int err; 196062306a36Sopenharmony_ci u16 ctl; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci err = ytphy_setup_master_slave(phydev); 196362306a36Sopenharmony_ci if (err < 0) 196462306a36Sopenharmony_ci return err; 196562306a36Sopenharmony_ci else if (err) 196662306a36Sopenharmony_ci changed = true; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE) { 196962306a36Sopenharmony_ci /* configures/forces speed/duplex from @phydev */ 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci return __phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK | 197462306a36Sopenharmony_ci BMCR_ISOLATE | BMCR_PDOWN), ctl); 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci err = ytphy_utp_config_advert(phydev); 197862306a36Sopenharmony_ci if (err < 0) /* error */ 197962306a36Sopenharmony_ci return err; 198062306a36Sopenharmony_ci else if (err) 198162306a36Sopenharmony_ci changed = true; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci return ytphy_check_and_restart_aneg(phydev, changed); 198462306a36Sopenharmony_ci} 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci/** 198762306a36Sopenharmony_ci * yt8521_config_aneg_paged() - switch reg space then call genphy_config_aneg 198862306a36Sopenharmony_ci * of one page 198962306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 199062306a36Sopenharmony_ci * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to 199162306a36Sopenharmony_ci * operate. 199262306a36Sopenharmony_ci * 199362306a36Sopenharmony_ci * returns 0 or negative errno code 199462306a36Sopenharmony_ci */ 199562306a36Sopenharmony_cistatic int yt8521_config_aneg_paged(struct phy_device *phydev, int page) 199662306a36Sopenharmony_ci{ 199762306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(fiber_supported); 199862306a36Sopenharmony_ci struct yt8521_priv *priv = phydev->priv; 199962306a36Sopenharmony_ci int old_page; 200062306a36Sopenharmony_ci int ret = 0; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci page &= YT8521_RSSR_SPACE_MASK; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci old_page = phy_select_page(phydev, page); 200562306a36Sopenharmony_ci if (old_page < 0) 200662306a36Sopenharmony_ci goto err_restore_page; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED, 200962306a36Sopenharmony_ci * phydev->advertising should be updated. 201062306a36Sopenharmony_ci */ 201162306a36Sopenharmony_ci if (priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) { 201262306a36Sopenharmony_ci linkmode_zero(fiber_supported); 201362306a36Sopenharmony_ci yt8521_prepare_fiber_features(phydev, fiber_supported); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci /* prepare fiber_supported, then setup advertising. */ 201662306a36Sopenharmony_ci if (page == YT8521_RSSR_FIBER_SPACE) { 201762306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, 201862306a36Sopenharmony_ci fiber_supported); 201962306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 202062306a36Sopenharmony_ci fiber_supported); 202162306a36Sopenharmony_ci linkmode_and(phydev->advertising, 202262306a36Sopenharmony_ci priv->combo_advertising, fiber_supported); 202362306a36Sopenharmony_ci } else { 202462306a36Sopenharmony_ci /* ETHTOOL_LINK_MODE_Autoneg_BIT is also used in utp */ 202562306a36Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 202662306a36Sopenharmony_ci fiber_supported); 202762306a36Sopenharmony_ci linkmode_andnot(phydev->advertising, 202862306a36Sopenharmony_ci priv->combo_advertising, 202962306a36Sopenharmony_ci fiber_supported); 203062306a36Sopenharmony_ci } 203162306a36Sopenharmony_ci } 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci if (page == YT8521_RSSR_FIBER_SPACE) 203462306a36Sopenharmony_ci ret = yt8521_fiber_config_aneg(phydev); 203562306a36Sopenharmony_ci else 203662306a36Sopenharmony_ci ret = ytphy_utp_config_aneg(phydev, false); 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cierr_restore_page: 203962306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, ret); 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci/** 204362306a36Sopenharmony_ci * yt8521_config_aneg() - change reg space then call yt8521_config_aneg_paged 204462306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 204562306a36Sopenharmony_ci * 204662306a36Sopenharmony_ci * returns 0 or negative errno code 204762306a36Sopenharmony_ci */ 204862306a36Sopenharmony_cistatic int yt8521_config_aneg(struct phy_device *phydev) 204962306a36Sopenharmony_ci{ 205062306a36Sopenharmony_ci struct yt8521_priv *priv = phydev->priv; 205162306a36Sopenharmony_ci int ret; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { 205462306a36Sopenharmony_ci ret = yt8521_config_aneg_paged(phydev, priv->reg_page); 205562306a36Sopenharmony_ci if (ret < 0) 205662306a36Sopenharmony_ci return ret; 205762306a36Sopenharmony_ci } else { 205862306a36Sopenharmony_ci /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED, 205962306a36Sopenharmony_ci * phydev->advertising need to be saved at first run. 206062306a36Sopenharmony_ci * Because it contains the advertising which supported by both 206162306a36Sopenharmony_ci * mac and yt8521(utp and fiber). 206262306a36Sopenharmony_ci */ 206362306a36Sopenharmony_ci if (linkmode_empty(priv->combo_advertising)) { 206462306a36Sopenharmony_ci linkmode_copy(priv->combo_advertising, 206562306a36Sopenharmony_ci phydev->advertising); 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_UTP_SPACE); 206962306a36Sopenharmony_ci if (ret < 0) 207062306a36Sopenharmony_ci return ret; 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_FIBER_SPACE); 207362306a36Sopenharmony_ci if (ret < 0) 207462306a36Sopenharmony_ci return ret; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci /* we don't known which will be link, so restore 207762306a36Sopenharmony_ci * phydev->advertising as default value. 207862306a36Sopenharmony_ci */ 207962306a36Sopenharmony_ci linkmode_copy(phydev->advertising, priv->combo_advertising); 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci return 0; 208262306a36Sopenharmony_ci} 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci/** 208562306a36Sopenharmony_ci * yt8521_aneg_done_paged() - determines the auto negotiation result of one 208662306a36Sopenharmony_ci * page. 208762306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 208862306a36Sopenharmony_ci * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to 208962306a36Sopenharmony_ci * operate. 209062306a36Sopenharmony_ci * 209162306a36Sopenharmony_ci * returns 0(no link)or 1(fiber or utp link) or negative errno code 209262306a36Sopenharmony_ci */ 209362306a36Sopenharmony_cistatic int yt8521_aneg_done_paged(struct phy_device *phydev, int page) 209462306a36Sopenharmony_ci{ 209562306a36Sopenharmony_ci int old_page; 209662306a36Sopenharmony_ci int ret = 0; 209762306a36Sopenharmony_ci int link; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); 210062306a36Sopenharmony_ci if (old_page < 0) 210162306a36Sopenharmony_ci goto err_restore_page; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); 210462306a36Sopenharmony_ci if (ret < 0) 210562306a36Sopenharmony_ci goto err_restore_page; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci link = !!(ret & YTPHY_SSR_LINK); 210862306a36Sopenharmony_ci ret = link; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_cierr_restore_page: 211162306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, ret); 211262306a36Sopenharmony_ci} 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci/** 211562306a36Sopenharmony_ci * yt8521_aneg_done() - determines the auto negotiation result 211662306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 211762306a36Sopenharmony_ci * 211862306a36Sopenharmony_ci * returns 0(no link)or 1(fiber or utp link) or negative errno code 211962306a36Sopenharmony_ci */ 212062306a36Sopenharmony_cistatic int yt8521_aneg_done(struct phy_device *phydev) 212162306a36Sopenharmony_ci{ 212262306a36Sopenharmony_ci struct yt8521_priv *priv = phydev->priv; 212362306a36Sopenharmony_ci int link_fiber = 0; 212462306a36Sopenharmony_ci int link_utp; 212562306a36Sopenharmony_ci int link; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { 212862306a36Sopenharmony_ci link = yt8521_aneg_done_paged(phydev, priv->reg_page); 212962306a36Sopenharmony_ci } else { 213062306a36Sopenharmony_ci link_utp = yt8521_aneg_done_paged(phydev, 213162306a36Sopenharmony_ci YT8521_RSSR_UTP_SPACE); 213262306a36Sopenharmony_ci if (link_utp < 0) 213362306a36Sopenharmony_ci return link_utp; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci if (!link_utp) { 213662306a36Sopenharmony_ci link_fiber = yt8521_aneg_done_paged(phydev, 213762306a36Sopenharmony_ci YT8521_RSSR_FIBER_SPACE); 213862306a36Sopenharmony_ci if (link_fiber < 0) 213962306a36Sopenharmony_ci return link_fiber; 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci link = link_fiber || link_utp; 214262306a36Sopenharmony_ci phydev_info(phydev, "%s, link_fiber: %d, link_utp: %d\n", 214362306a36Sopenharmony_ci __func__, link_fiber, link_utp); 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci return link; 214762306a36Sopenharmony_ci} 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci/** 215062306a36Sopenharmony_ci * ytphy_utp_read_abilities - read PHY abilities from Clause 22 registers 215162306a36Sopenharmony_ci * @phydev: target phy_device struct 215262306a36Sopenharmony_ci * 215362306a36Sopenharmony_ci * NOTE: Reads the PHY's abilities and populates 215462306a36Sopenharmony_ci * phydev->supported accordingly. 215562306a36Sopenharmony_ci * The caller must have taken the MDIO bus lock. 215662306a36Sopenharmony_ci * 215762306a36Sopenharmony_ci * returns 0 or negative errno code 215862306a36Sopenharmony_ci */ 215962306a36Sopenharmony_cistatic int ytphy_utp_read_abilities(struct phy_device *phydev) 216062306a36Sopenharmony_ci{ 216162306a36Sopenharmony_ci int val; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci linkmode_set_bit_array(phy_basic_ports_array, 216462306a36Sopenharmony_ci ARRAY_SIZE(phy_basic_ports_array), 216562306a36Sopenharmony_ci phydev->supported); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci val = __phy_read(phydev, MII_BMSR); 216862306a36Sopenharmony_ci if (val < 0) 216962306a36Sopenharmony_ci return val; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, 217262306a36Sopenharmony_ci val & BMSR_ANEGCAPABLE); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, 217562306a36Sopenharmony_ci val & BMSR_100FULL); 217662306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, 217762306a36Sopenharmony_ci val & BMSR_100HALF); 217862306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, 217962306a36Sopenharmony_ci val & BMSR_10FULL); 218062306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, 218162306a36Sopenharmony_ci val & BMSR_10HALF); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci if (val & BMSR_ESTATEN) { 218462306a36Sopenharmony_ci val = __phy_read(phydev, MII_ESTATUS); 218562306a36Sopenharmony_ci if (val < 0) 218662306a36Sopenharmony_ci return val; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 218962306a36Sopenharmony_ci phydev->supported, val & ESTATUS_1000_TFULL); 219062306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 219162306a36Sopenharmony_ci phydev->supported, val & ESTATUS_1000_THALF); 219262306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 219362306a36Sopenharmony_ci phydev->supported, val & ESTATUS_1000_XFULL); 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci return 0; 219762306a36Sopenharmony_ci} 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci/** 220062306a36Sopenharmony_ci * yt8521_get_features_paged() - read supported link modes for one page 220162306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device 220262306a36Sopenharmony_ci * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to 220362306a36Sopenharmony_ci * operate. 220462306a36Sopenharmony_ci * 220562306a36Sopenharmony_ci * returns 0 or negative errno code 220662306a36Sopenharmony_ci */ 220762306a36Sopenharmony_cistatic int yt8521_get_features_paged(struct phy_device *phydev, int page) 220862306a36Sopenharmony_ci{ 220962306a36Sopenharmony_ci int old_page; 221062306a36Sopenharmony_ci int ret = 0; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci page &= YT8521_RSSR_SPACE_MASK; 221362306a36Sopenharmony_ci old_page = phy_select_page(phydev, page); 221462306a36Sopenharmony_ci if (old_page < 0) 221562306a36Sopenharmony_ci goto err_restore_page; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci if (page == YT8521_RSSR_FIBER_SPACE) { 221862306a36Sopenharmony_ci linkmode_zero(phydev->supported); 221962306a36Sopenharmony_ci yt8521_prepare_fiber_features(phydev, phydev->supported); 222062306a36Sopenharmony_ci } else { 222162306a36Sopenharmony_ci ret = ytphy_utp_read_abilities(phydev); 222262306a36Sopenharmony_ci if (ret < 0) 222362306a36Sopenharmony_ci goto err_restore_page; 222462306a36Sopenharmony_ci } 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_cierr_restore_page: 222762306a36Sopenharmony_ci return phy_restore_page(phydev, old_page, ret); 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci/** 223162306a36Sopenharmony_ci * yt8521_get_features - switch reg space then call yt8521_get_features_paged 223262306a36Sopenharmony_ci * @phydev: target phy_device struct 223362306a36Sopenharmony_ci * 223462306a36Sopenharmony_ci * returns 0 or negative errno code 223562306a36Sopenharmony_ci */ 223662306a36Sopenharmony_cistatic int yt8521_get_features(struct phy_device *phydev) 223762306a36Sopenharmony_ci{ 223862306a36Sopenharmony_ci struct yt8521_priv *priv = phydev->priv; 223962306a36Sopenharmony_ci int ret; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { 224262306a36Sopenharmony_ci ret = yt8521_get_features_paged(phydev, priv->reg_page); 224362306a36Sopenharmony_ci } else { 224462306a36Sopenharmony_ci ret = yt8521_get_features_paged(phydev, 224562306a36Sopenharmony_ci YT8521_RSSR_UTP_SPACE); 224662306a36Sopenharmony_ci if (ret < 0) 224762306a36Sopenharmony_ci return ret; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci /* add fiber's features to phydev->supported */ 225062306a36Sopenharmony_ci yt8521_prepare_fiber_features(phydev, phydev->supported); 225162306a36Sopenharmony_ci } 225262306a36Sopenharmony_ci return ret; 225362306a36Sopenharmony_ci} 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_cistatic struct phy_driver motorcomm_phy_drvs[] = { 225662306a36Sopenharmony_ci { 225762306a36Sopenharmony_ci PHY_ID_MATCH_EXACT(PHY_ID_YT8511), 225862306a36Sopenharmony_ci .name = "YT8511 Gigabit Ethernet", 225962306a36Sopenharmony_ci .config_init = yt8511_config_init, 226062306a36Sopenharmony_ci .suspend = genphy_suspend, 226162306a36Sopenharmony_ci .resume = genphy_resume, 226262306a36Sopenharmony_ci .read_page = yt8511_read_page, 226362306a36Sopenharmony_ci .write_page = yt8511_write_page, 226462306a36Sopenharmony_ci }, 226562306a36Sopenharmony_ci { 226662306a36Sopenharmony_ci PHY_ID_MATCH_EXACT(PHY_ID_YT8521), 226762306a36Sopenharmony_ci .name = "YT8521 Gigabit Ethernet", 226862306a36Sopenharmony_ci .get_features = yt8521_get_features, 226962306a36Sopenharmony_ci .probe = yt8521_probe, 227062306a36Sopenharmony_ci .read_page = yt8521_read_page, 227162306a36Sopenharmony_ci .write_page = yt8521_write_page, 227262306a36Sopenharmony_ci .get_wol = ytphy_get_wol, 227362306a36Sopenharmony_ci .set_wol = ytphy_set_wol, 227462306a36Sopenharmony_ci .config_aneg = yt8521_config_aneg, 227562306a36Sopenharmony_ci .aneg_done = yt8521_aneg_done, 227662306a36Sopenharmony_ci .config_init = yt8521_config_init, 227762306a36Sopenharmony_ci .read_status = yt8521_read_status, 227862306a36Sopenharmony_ci .soft_reset = yt8521_soft_reset, 227962306a36Sopenharmony_ci .suspend = yt8521_suspend, 228062306a36Sopenharmony_ci .resume = yt8521_resume, 228162306a36Sopenharmony_ci }, 228262306a36Sopenharmony_ci { 228362306a36Sopenharmony_ci PHY_ID_MATCH_EXACT(PHY_ID_YT8531), 228462306a36Sopenharmony_ci .name = "YT8531 Gigabit Ethernet", 228562306a36Sopenharmony_ci .probe = yt8531_probe, 228662306a36Sopenharmony_ci .config_init = yt8531_config_init, 228762306a36Sopenharmony_ci .suspend = genphy_suspend, 228862306a36Sopenharmony_ci .resume = genphy_resume, 228962306a36Sopenharmony_ci .get_wol = ytphy_get_wol, 229062306a36Sopenharmony_ci .set_wol = yt8531_set_wol, 229162306a36Sopenharmony_ci .link_change_notify = yt8531_link_change_notify, 229262306a36Sopenharmony_ci }, 229362306a36Sopenharmony_ci { 229462306a36Sopenharmony_ci PHY_ID_MATCH_EXACT(PHY_ID_YT8531S), 229562306a36Sopenharmony_ci .name = "YT8531S Gigabit Ethernet", 229662306a36Sopenharmony_ci .get_features = yt8521_get_features, 229762306a36Sopenharmony_ci .probe = yt8521_probe, 229862306a36Sopenharmony_ci .read_page = yt8521_read_page, 229962306a36Sopenharmony_ci .write_page = yt8521_write_page, 230062306a36Sopenharmony_ci .get_wol = ytphy_get_wol, 230162306a36Sopenharmony_ci .set_wol = ytphy_set_wol, 230262306a36Sopenharmony_ci .config_aneg = yt8521_config_aneg, 230362306a36Sopenharmony_ci .aneg_done = yt8521_aneg_done, 230462306a36Sopenharmony_ci .config_init = yt8521_config_init, 230562306a36Sopenharmony_ci .read_status = yt8521_read_status, 230662306a36Sopenharmony_ci .soft_reset = yt8521_soft_reset, 230762306a36Sopenharmony_ci .suspend = yt8521_suspend, 230862306a36Sopenharmony_ci .resume = yt8521_resume, 230962306a36Sopenharmony_ci }, 231062306a36Sopenharmony_ci}; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_cimodule_phy_driver(motorcomm_phy_drvs); 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ciMODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver"); 231562306a36Sopenharmony_ciMODULE_AUTHOR("Peter Geis"); 231662306a36Sopenharmony_ciMODULE_AUTHOR("Frank"); 231762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_cistatic const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { 232062306a36Sopenharmony_ci { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, 232162306a36Sopenharmony_ci { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, 232262306a36Sopenharmony_ci { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) }, 232362306a36Sopenharmony_ci { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, 232462306a36Sopenharmony_ci { /* sentinel */ } 232562306a36Sopenharmony_ci}; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, motorcomm_tbl); 2328