xref: /openbmc/linux/drivers/net/phy/motorcomm.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
148e8c6f1SPeter Geis // SPDX-License-Identifier: GPL-2.0+
248e8c6f1SPeter Geis /*
34ac94f72SFrank Sae  * Motorcomm 8511/8521/8531/8531S PHY driver.
448e8c6f1SPeter Geis  *
548e8c6f1SPeter Geis  * Author: Peter Geis <pgwipeout@gmail.com>
670479a40SFrank  * Author: Frank <Frank.Sae@motor-comm.com>
748e8c6f1SPeter Geis  */
848e8c6f1SPeter Geis 
970479a40SFrank #include <linux/etherdevice.h>
1048e8c6f1SPeter Geis #include <linux/kernel.h>
1148e8c6f1SPeter Geis #include <linux/module.h>
1248e8c6f1SPeter Geis #include <linux/phy.h>
13a6e68f0fSFrank Sae #include <linux/of.h>
1448e8c6f1SPeter Geis 
1548e8c6f1SPeter Geis #define PHY_ID_YT8511		0x0000010a
163c1dc221SFrank Sae #define PHY_ID_YT8521		0x0000011a
174ac94f72SFrank Sae #define PHY_ID_YT8531		0x4f51e91b
183c1dc221SFrank Sae #define PHY_ID_YT8531S		0x4f51e91a
1970479a40SFrank 
20813abcd9SFrank /* YT8521/YT8531S Register Overview
2170479a40SFrank  *	UTP Register space	|	FIBER Register space
2270479a40SFrank  *  ------------------------------------------------------------
2370479a40SFrank  * |	UTP MII			|	FIBER MII		|
2470479a40SFrank  * |	UTP MMD			|				|
2570479a40SFrank  * |	UTP Extended		|	FIBER Extended		|
2670479a40SFrank  *  ------------------------------------------------------------
2770479a40SFrank  * |			Common Extended				|
2870479a40SFrank  *  ------------------------------------------------------------
2970479a40SFrank  */
3070479a40SFrank 
3170479a40SFrank /* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
3270479a40SFrank 
3370479a40SFrank /* Specific Function Control Register */
3470479a40SFrank #define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG	0x10
3570479a40SFrank 
3670479a40SFrank /* 2b00 Manual MDI configuration
3770479a40SFrank  * 2b01 Manual MDIX configuration
3870479a40SFrank  * 2b10 Reserved
3970479a40SFrank  * 2b11 Enable automatic crossover for all modes  *default*
4070479a40SFrank  */
4170479a40SFrank #define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK	(BIT(6) | BIT(5))
4270479a40SFrank #define YTPHY_SFCR_CROSSOVER_EN			BIT(3)
4370479a40SFrank #define YTPHY_SFCR_SQE_TEST_EN			BIT(2)
4470479a40SFrank #define YTPHY_SFCR_POLARITY_REVERSAL_EN		BIT(1)
4570479a40SFrank #define YTPHY_SFCR_JABBER_DIS			BIT(0)
4670479a40SFrank 
4770479a40SFrank /* Specific Status Register */
4870479a40SFrank #define YTPHY_SPECIFIC_STATUS_REG		0x11
4970479a40SFrank #define YTPHY_SSR_SPEED_MODE_OFFSET		14
5070479a40SFrank 
5170479a40SFrank #define YTPHY_SSR_SPEED_MODE_MASK		(BIT(15) | BIT(14))
5270479a40SFrank #define YTPHY_SSR_SPEED_10M			0x0
5370479a40SFrank #define YTPHY_SSR_SPEED_100M			0x1
5470479a40SFrank #define YTPHY_SSR_SPEED_1000M			0x2
5570479a40SFrank #define YTPHY_SSR_DUPLEX_OFFSET			13
5670479a40SFrank #define YTPHY_SSR_DUPLEX			BIT(13)
5770479a40SFrank #define YTPHY_SSR_PAGE_RECEIVED			BIT(12)
5870479a40SFrank #define YTPHY_SSR_SPEED_DUPLEX_RESOLVED		BIT(11)
5970479a40SFrank #define YTPHY_SSR_LINK				BIT(10)
6070479a40SFrank #define YTPHY_SSR_MDIX_CROSSOVER		BIT(6)
6170479a40SFrank #define YTPHY_SSR_DOWNGRADE			BIT(5)
6270479a40SFrank #define YTPHY_SSR_TRANSMIT_PAUSE		BIT(3)
6370479a40SFrank #define YTPHY_SSR_RECEIVE_PAUSE			BIT(2)
6470479a40SFrank #define YTPHY_SSR_POLARITY			BIT(1)
6570479a40SFrank #define YTPHY_SSR_JABBER			BIT(0)
6670479a40SFrank 
6770479a40SFrank /* Interrupt enable Register */
6870479a40SFrank #define YTPHY_INTERRUPT_ENABLE_REG		0x12
6970479a40SFrank #define YTPHY_IER_WOL				BIT(6)
7070479a40SFrank 
7170479a40SFrank /* Interrupt Status Register */
7270479a40SFrank #define YTPHY_INTERRUPT_STATUS_REG		0x13
7370479a40SFrank #define YTPHY_ISR_AUTONEG_ERR			BIT(15)
7470479a40SFrank #define YTPHY_ISR_SPEED_CHANGED			BIT(14)
7570479a40SFrank #define YTPHY_ISR_DUPLEX_CHANGED		BIT(13)
7670479a40SFrank #define YTPHY_ISR_PAGE_RECEIVED			BIT(12)
7770479a40SFrank #define YTPHY_ISR_LINK_FAILED			BIT(11)
7870479a40SFrank #define YTPHY_ISR_LINK_SUCCESSED		BIT(10)
7970479a40SFrank #define YTPHY_ISR_WOL				BIT(6)
8070479a40SFrank #define YTPHY_ISR_WIRESPEED_DOWNGRADE		BIT(5)
8170479a40SFrank #define YTPHY_ISR_SERDES_LINK_FAILED		BIT(3)
8270479a40SFrank #define YTPHY_ISR_SERDES_LINK_SUCCESSED		BIT(2)
8370479a40SFrank #define YTPHY_ISR_POLARITY_CHANGED		BIT(1)
8470479a40SFrank #define YTPHY_ISR_JABBER_HAPPENED		BIT(0)
8570479a40SFrank 
8670479a40SFrank /* Speed Auto Downgrade Control Register */
8770479a40SFrank #define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG	0x14
8870479a40SFrank #define YTPHY_SADCR_SPEED_DOWNGRADE_EN		BIT(5)
8970479a40SFrank 
9070479a40SFrank /* If these bits are set to 3, the PHY attempts five times ( 3(set value) +
9170479a40SFrank  * additional 2) before downgrading, default 0x3
9270479a40SFrank  */
9370479a40SFrank #define YTPHY_SADCR_SPEED_RETRY_LIMIT		(0x3 << 2)
9470479a40SFrank 
9570479a40SFrank /* Rx Error Counter Register */
9670479a40SFrank #define YTPHY_RX_ERROR_COUNTER_REG		0x15
9770479a40SFrank 
9870479a40SFrank /* Extended Register's Address Offset Register */
9970479a40SFrank #define YTPHY_PAGE_SELECT			0x1E
10070479a40SFrank 
10170479a40SFrank /* Extended Register's Data Register */
10270479a40SFrank #define YTPHY_PAGE_DATA				0x1F
10370479a40SFrank 
10470479a40SFrank /* FIBER Auto-Negotiation link partner ability */
10570479a40SFrank #define YTPHY_FLPA_PAUSE			(0x3 << 7)
10670479a40SFrank #define YTPHY_FLPA_ASYM_PAUSE			(0x2 << 7)
10748e8c6f1SPeter Geis 
10848e8c6f1SPeter Geis #define YT8511_PAGE_SELECT	0x1e
10948e8c6f1SPeter Geis #define YT8511_PAGE		0x1f
11048e8c6f1SPeter Geis #define YT8511_EXT_CLK_GATE	0x0c
11148e8c6f1SPeter Geis #define YT8511_EXT_DELAY_DRIVE	0x0d
11248e8c6f1SPeter Geis #define YT8511_EXT_SLEEP_CTRL	0x27
11348e8c6f1SPeter Geis 
11448e8c6f1SPeter Geis /* 2b00 25m from pll
11548e8c6f1SPeter Geis  * 2b01 25m from xtl *default*
11648e8c6f1SPeter Geis  * 2b10 62.m from pll
11748e8c6f1SPeter Geis  * 2b11 125m from pll
11848e8c6f1SPeter Geis  */
11948e8c6f1SPeter Geis #define YT8511_CLK_125M		(BIT(2) | BIT(1))
12048e8c6f1SPeter Geis #define YT8511_PLLON_SLP	BIT(14)
12148e8c6f1SPeter Geis 
12248e8c6f1SPeter Geis /* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */
12348e8c6f1SPeter Geis #define YT8511_DELAY_RX		BIT(0)
12448e8c6f1SPeter Geis 
12548e8c6f1SPeter Geis /* TX Gig-E Delay is bits 7:4, default 0x5
12648e8c6f1SPeter Geis  * TX Fast-E Delay is bits 15:12, default 0xf
12748e8c6f1SPeter Geis  * Delay = 150ps * N - 250ps
12848e8c6f1SPeter Geis  * On = 2000ps, off = 50ps
12948e8c6f1SPeter Geis  */
13048e8c6f1SPeter Geis #define YT8511_DELAY_GE_TX_EN	(0xf << 4)
13148e8c6f1SPeter Geis #define YT8511_DELAY_GE_TX_DIS	(0x2 << 4)
13248e8c6f1SPeter Geis #define YT8511_DELAY_FE_TX_EN	(0xf << 12)
13348e8c6f1SPeter Geis #define YT8511_DELAY_FE_TX_DIS	(0x2 << 12)
13448e8c6f1SPeter Geis 
13570479a40SFrank /* Extended register is different from MMD Register and MII Register.
13670479a40SFrank  * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to
13770479a40SFrank  * operate extended register.
13870479a40SFrank  * Extended Register  start
13970479a40SFrank  */
14070479a40SFrank 
14170479a40SFrank /* Phy gmii clock gating Register */
14270479a40SFrank #define YT8521_CLOCK_GATING_REG			0xC
14370479a40SFrank #define YT8521_CGR_RX_CLK_EN			BIT(12)
14470479a40SFrank 
14570479a40SFrank #define YT8521_EXTREG_SLEEP_CONTROL1_REG	0x27
14670479a40SFrank #define YT8521_ESC1R_SLEEP_SW			BIT(15)
14770479a40SFrank #define YT8521_ESC1R_PLLON_SLP			BIT(14)
14870479a40SFrank 
14970479a40SFrank /* Phy fiber Link timer cfg2 Register */
15070479a40SFrank #define YT8521_LINK_TIMER_CFG2_REG		0xA5
15170479a40SFrank #define YT8521_LTCR_EN_AUTOSEN			BIT(15)
15270479a40SFrank 
153813abcd9SFrank /* 0xA000, 0xA001, 0xA003, 0xA006 ~ 0xA00A and 0xA012 are common ext registers
15470479a40SFrank  * of yt8521 phy. There is no need to switch reg space when operating these
15570479a40SFrank  * registers.
15670479a40SFrank  */
15770479a40SFrank 
15870479a40SFrank #define YT8521_REG_SPACE_SELECT_REG		0xA000
15970479a40SFrank #define YT8521_RSSR_SPACE_MASK			BIT(1)
16070479a40SFrank #define YT8521_RSSR_FIBER_SPACE			(0x1 << 1)
16170479a40SFrank #define YT8521_RSSR_UTP_SPACE			(0x0 << 1)
16270479a40SFrank #define YT8521_RSSR_TO_BE_ARBITRATED		(0xFF)
16370479a40SFrank 
16470479a40SFrank #define YT8521_CHIP_CONFIG_REG			0xA001
16570479a40SFrank #define YT8521_CCR_SW_RST			BIT(15)
166*7a561e93SSamin Guo #define YT8531_RGMII_LDO_VOL_MASK		GENMASK(5, 4)
167*7a561e93SSamin Guo #define YT8531_LDO_VOL_3V3			0x0
168*7a561e93SSamin Guo #define YT8531_LDO_VOL_1V8			0x2
169*7a561e93SSamin Guo 
1704869a146SFrank Sae /* 1b0 disable 1.9ns rxc clock delay  *default*
1714869a146SFrank Sae  * 1b1 enable 1.9ns rxc clock delay
1724869a146SFrank Sae  */
1734869a146SFrank Sae #define YT8521_CCR_RXC_DLY_EN			BIT(8)
1744869a146SFrank Sae #define YT8521_CCR_RXC_DLY_1_900_NS		1900
17570479a40SFrank 
17670479a40SFrank #define YT8521_CCR_MODE_SEL_MASK		(BIT(2) | BIT(1) | BIT(0))
17770479a40SFrank #define YT8521_CCR_MODE_UTP_TO_RGMII		0
17870479a40SFrank #define YT8521_CCR_MODE_FIBER_TO_RGMII		1
17970479a40SFrank #define YT8521_CCR_MODE_UTP_FIBER_TO_RGMII	2
18070479a40SFrank #define YT8521_CCR_MODE_UTP_TO_SGMII		3
18170479a40SFrank #define YT8521_CCR_MODE_SGPHY_TO_RGMAC		4
18270479a40SFrank #define YT8521_CCR_MODE_SGMAC_TO_RGPHY		5
18370479a40SFrank #define YT8521_CCR_MODE_UTP_TO_FIBER_AUTO	6
18470479a40SFrank #define YT8521_CCR_MODE_UTP_TO_FIBER_FORCE	7
18570479a40SFrank 
18670479a40SFrank /* 3 phy polling modes,poll mode combines utp and fiber mode*/
18770479a40SFrank #define YT8521_MODE_FIBER			0x1
18870479a40SFrank #define YT8521_MODE_UTP				0x2
18970479a40SFrank #define YT8521_MODE_POLL			0x3
19070479a40SFrank 
19170479a40SFrank #define YT8521_RGMII_CONFIG1_REG		0xA003
1924869a146SFrank Sae /* 1b0 use original tx_clk_rgmii  *default*
1934869a146SFrank Sae  * 1b1 use inverted tx_clk_rgmii.
1944869a146SFrank Sae  */
1954869a146SFrank Sae #define YT8521_RC1R_TX_CLK_SEL_INVERTED		BIT(14)
1964869a146SFrank Sae #define YT8521_RC1R_RX_DELAY_MASK		GENMASK(13, 10)
1974869a146SFrank Sae #define YT8521_RC1R_FE_TX_DELAY_MASK		GENMASK(7, 4)
1984869a146SFrank Sae #define YT8521_RC1R_GE_TX_DELAY_MASK		GENMASK(3, 0)
1994869a146SFrank Sae #define YT8521_RC1R_RGMII_0_000_NS		0
2004869a146SFrank Sae #define YT8521_RC1R_RGMII_0_150_NS		1
2014869a146SFrank Sae #define YT8521_RC1R_RGMII_0_300_NS		2
2024869a146SFrank Sae #define YT8521_RC1R_RGMII_0_450_NS		3
2034869a146SFrank Sae #define YT8521_RC1R_RGMII_0_600_NS		4
2044869a146SFrank Sae #define YT8521_RC1R_RGMII_0_750_NS		5
2054869a146SFrank Sae #define YT8521_RC1R_RGMII_0_900_NS		6
2064869a146SFrank Sae #define YT8521_RC1R_RGMII_1_050_NS		7
2074869a146SFrank Sae #define YT8521_RC1R_RGMII_1_200_NS		8
2084869a146SFrank Sae #define YT8521_RC1R_RGMII_1_350_NS		9
2094869a146SFrank Sae #define YT8521_RC1R_RGMII_1_500_NS		10
2104869a146SFrank Sae #define YT8521_RC1R_RGMII_1_650_NS		11
2114869a146SFrank Sae #define YT8521_RC1R_RGMII_1_800_NS		12
2124869a146SFrank Sae #define YT8521_RC1R_RGMII_1_950_NS		13
2134869a146SFrank Sae #define YT8521_RC1R_RGMII_2_100_NS		14
2144869a146SFrank Sae #define YT8521_RC1R_RGMII_2_250_NS		15
21570479a40SFrank 
21670479a40SFrank #define YTPHY_MISC_CONFIG_REG			0xA006
21770479a40SFrank #define YTPHY_MCR_FIBER_SPEED_MASK		BIT(0)
21870479a40SFrank #define YTPHY_MCR_FIBER_1000BX			(0x1 << 0)
21970479a40SFrank #define YTPHY_MCR_FIBER_100FX			(0x0 << 0)
22070479a40SFrank 
22170479a40SFrank /* WOL MAC ADDR: MACADDR2(highest), MACADDR1(middle), MACADDR0(lowest) */
22270479a40SFrank #define YTPHY_WOL_MACADDR2_REG			0xA007
22370479a40SFrank #define YTPHY_WOL_MACADDR1_REG			0xA008
22470479a40SFrank #define YTPHY_WOL_MACADDR0_REG			0xA009
22570479a40SFrank 
22670479a40SFrank #define YTPHY_WOL_CONFIG_REG			0xA00A
22770479a40SFrank #define YTPHY_WCR_INTR_SEL			BIT(6)
22870479a40SFrank #define YTPHY_WCR_ENABLE			BIT(3)
22970479a40SFrank 
23070479a40SFrank /* 2b00 84ms
23170479a40SFrank  * 2b01 168ms  *default*
23270479a40SFrank  * 2b10 336ms
23370479a40SFrank  * 2b11 672ms
23470479a40SFrank  */
23570479a40SFrank #define YTPHY_WCR_PULSE_WIDTH_MASK		(BIT(2) | BIT(1))
23670479a40SFrank #define YTPHY_WCR_PULSE_WIDTH_672MS		(BIT(2) | BIT(1))
23770479a40SFrank 
23870479a40SFrank /* 1b0 Interrupt and WOL events is level triggered and active LOW  *default*
23970479a40SFrank  * 1b1 Interrupt and WOL events is pulse triggered and active LOW
24070479a40SFrank  */
24170479a40SFrank #define YTPHY_WCR_TYPE_PULSE			BIT(0)
24270479a40SFrank 
243*7a561e93SSamin Guo #define YTPHY_PAD_DRIVE_STRENGTH_REG		0xA010
244*7a561e93SSamin Guo #define YT8531_RGMII_RXC_DS_MASK		GENMASK(15, 13)
245*7a561e93SSamin Guo #define YT8531_RGMII_RXD_DS_HI_MASK		BIT(12)		/* Bit 2 of rxd_ds */
246*7a561e93SSamin Guo #define YT8531_RGMII_RXD_DS_LOW_MASK		GENMASK(5, 4)	/* Bit 1/0 of rxd_ds */
247*7a561e93SSamin Guo #define YT8531_RGMII_RX_DS_DEFAULT		0x3
248*7a561e93SSamin Guo 
2494869a146SFrank Sae #define YTPHY_SYNCE_CFG_REG			0xA012
2504869a146SFrank Sae #define YT8521_SCR_SYNCE_ENABLE			BIT(5)
2514869a146SFrank Sae /* 1b0 output 25m clock
2524869a146SFrank Sae  * 1b1 output 125m clock  *default*
2534869a146SFrank Sae  */
2544869a146SFrank Sae #define YT8521_SCR_CLK_FRE_SEL_125M		BIT(3)
2554869a146SFrank Sae #define YT8521_SCR_CLK_SRC_MASK			GENMASK(2, 1)
2564869a146SFrank Sae #define YT8521_SCR_CLK_SRC_PLL_125M		0
2574869a146SFrank Sae #define YT8521_SCR_CLK_SRC_UTP_RX		1
2584869a146SFrank Sae #define YT8521_SCR_CLK_SRC_SDS_RX		2
2594869a146SFrank Sae #define YT8521_SCR_CLK_SRC_REF_25M		3
2604869a146SFrank Sae #define YT8531_SCR_SYNCE_ENABLE			BIT(6)
2614869a146SFrank Sae /* 1b0 output 25m clock   *default*
2624869a146SFrank Sae  * 1b1 output 125m clock
2634869a146SFrank Sae  */
2644869a146SFrank Sae #define YT8531_SCR_CLK_FRE_SEL_125M		BIT(4)
2654869a146SFrank Sae #define YT8531_SCR_CLK_SRC_MASK			GENMASK(3, 1)
2664869a146SFrank Sae #define YT8531_SCR_CLK_SRC_PLL_125M		0
2674869a146SFrank Sae #define YT8531_SCR_CLK_SRC_UTP_RX		1
2684869a146SFrank Sae #define YT8531_SCR_CLK_SRC_SDS_RX		2
2694869a146SFrank Sae #define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL	3
2704869a146SFrank Sae #define YT8531_SCR_CLK_SRC_REF_25M		4
2714869a146SFrank Sae #define YT8531_SCR_CLK_SRC_SSC_25M		5
272813abcd9SFrank 
27370479a40SFrank /* Extended Register  end */
27470479a40SFrank 
275a6e68f0fSFrank Sae #define YTPHY_DTS_OUTPUT_CLK_DIS		0
276a6e68f0fSFrank Sae #define YTPHY_DTS_OUTPUT_CLK_25M		25000000
277a6e68f0fSFrank Sae #define YTPHY_DTS_OUTPUT_CLK_125M		125000000
278a6e68f0fSFrank Sae 
27970479a40SFrank struct yt8521_priv {
28070479a40SFrank 	/* combo_advertising is used for case of YT8521 in combo mode,
28170479a40SFrank 	 * this means that yt8521 may work in utp or fiber mode which depends
28270479a40SFrank 	 * on which media is connected (YT8521_RSSR_TO_BE_ARBITRATED).
28370479a40SFrank 	 */
28470479a40SFrank 	__ETHTOOL_DECLARE_LINK_MODE_MASK(combo_advertising);
28570479a40SFrank 
28670479a40SFrank 	/* YT8521_MODE_FIBER / YT8521_MODE_UTP / YT8521_MODE_POLL*/
28770479a40SFrank 	u8 polling_mode;
28870479a40SFrank 	u8 strap_mode; /* 8 working modes  */
28970479a40SFrank 	/* current reg page of yt8521 phy:
29070479a40SFrank 	 * YT8521_RSSR_UTP_SPACE
29170479a40SFrank 	 * YT8521_RSSR_FIBER_SPACE
29270479a40SFrank 	 * YT8521_RSSR_TO_BE_ARBITRATED
29370479a40SFrank 	 */
29470479a40SFrank 	u8 reg_page;
29570479a40SFrank };
29670479a40SFrank 
29770479a40SFrank /**
29870479a40SFrank  * ytphy_read_ext() - read a PHY's extended register
29970479a40SFrank  * @phydev: a pointer to a &struct phy_device
30070479a40SFrank  * @regnum: register number to read
30170479a40SFrank  *
30270479a40SFrank  * NOTE:The caller must have taken the MDIO bus lock.
30370479a40SFrank  *
30470479a40SFrank  * returns the value of regnum reg or negative error code
30570479a40SFrank  */
ytphy_read_ext(struct phy_device * phydev,u16 regnum)30670479a40SFrank static int ytphy_read_ext(struct phy_device *phydev, u16 regnum)
30770479a40SFrank {
30870479a40SFrank 	int ret;
30970479a40SFrank 
31070479a40SFrank 	ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
31170479a40SFrank 	if (ret < 0)
31270479a40SFrank 		return ret;
31370479a40SFrank 
31470479a40SFrank 	return __phy_read(phydev, YTPHY_PAGE_DATA);
31570479a40SFrank }
31670479a40SFrank 
31770479a40SFrank /**
31870479a40SFrank  * ytphy_read_ext_with_lock() - read a PHY's extended register
31970479a40SFrank  * @phydev: a pointer to a &struct phy_device
32070479a40SFrank  * @regnum: register number to read
32170479a40SFrank  *
32270479a40SFrank  * returns the value of regnum reg or negative error code
32370479a40SFrank  */
ytphy_read_ext_with_lock(struct phy_device * phydev,u16 regnum)32470479a40SFrank static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
32570479a40SFrank {
32670479a40SFrank 	int ret;
32770479a40SFrank 
32870479a40SFrank 	phy_lock_mdio_bus(phydev);
32970479a40SFrank 	ret = ytphy_read_ext(phydev, regnum);
33070479a40SFrank 	phy_unlock_mdio_bus(phydev);
33170479a40SFrank 
33270479a40SFrank 	return ret;
33370479a40SFrank }
33470479a40SFrank 
33570479a40SFrank /**
33670479a40SFrank  * ytphy_write_ext() - write a PHY's extended register
33770479a40SFrank  * @phydev: a pointer to a &struct phy_device
33870479a40SFrank  * @regnum: register number to write
33970479a40SFrank  * @val: value to write to @regnum
34070479a40SFrank  *
34170479a40SFrank  * NOTE:The caller must have taken the MDIO bus lock.
34270479a40SFrank  *
34370479a40SFrank  * returns 0 or negative error code
34470479a40SFrank  */
ytphy_write_ext(struct phy_device * phydev,u16 regnum,u16 val)34570479a40SFrank static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val)
34670479a40SFrank {
34770479a40SFrank 	int ret;
34870479a40SFrank 
34970479a40SFrank 	ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
35070479a40SFrank 	if (ret < 0)
35170479a40SFrank 		return ret;
35270479a40SFrank 
35370479a40SFrank 	return __phy_write(phydev, YTPHY_PAGE_DATA, val);
35470479a40SFrank }
35570479a40SFrank 
35670479a40SFrank /**
35770479a40SFrank  * ytphy_write_ext_with_lock() - write a PHY's extended register
35870479a40SFrank  * @phydev: a pointer to a &struct phy_device
35970479a40SFrank  * @regnum: register number to write
36070479a40SFrank  * @val: value to write to @regnum
36170479a40SFrank  *
36270479a40SFrank  * returns 0 or negative error code
36370479a40SFrank  */
ytphy_write_ext_with_lock(struct phy_device * phydev,u16 regnum,u16 val)36470479a40SFrank static int ytphy_write_ext_with_lock(struct phy_device *phydev, u16 regnum,
36570479a40SFrank 				     u16 val)
36670479a40SFrank {
36770479a40SFrank 	int ret;
36870479a40SFrank 
36970479a40SFrank 	phy_lock_mdio_bus(phydev);
37070479a40SFrank 	ret = ytphy_write_ext(phydev, regnum, val);
37170479a40SFrank 	phy_unlock_mdio_bus(phydev);
37270479a40SFrank 
37370479a40SFrank 	return ret;
37470479a40SFrank }
37570479a40SFrank 
37670479a40SFrank /**
37770479a40SFrank  * ytphy_modify_ext() - bits modify a PHY's extended register
37870479a40SFrank  * @phydev: a pointer to a &struct phy_device
37970479a40SFrank  * @regnum: register number to write
38070479a40SFrank  * @mask: bit mask of bits to clear
38170479a40SFrank  * @set: bit mask of bits to set
38270479a40SFrank  *
38370479a40SFrank  * NOTE: Convenience function which allows a PHY's extended register to be
38470479a40SFrank  * modified as new register value = (old register value & ~mask) | set.
38570479a40SFrank  * The caller must have taken the MDIO bus lock.
38670479a40SFrank  *
38770479a40SFrank  * returns 0 or negative error code
38870479a40SFrank  */
ytphy_modify_ext(struct phy_device * phydev,u16 regnum,u16 mask,u16 set)38970479a40SFrank static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask,
39070479a40SFrank 			    u16 set)
39170479a40SFrank {
39270479a40SFrank 	int ret;
39370479a40SFrank 
39470479a40SFrank 	ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
39570479a40SFrank 	if (ret < 0)
39670479a40SFrank 		return ret;
39770479a40SFrank 
39870479a40SFrank 	return __phy_modify(phydev, YTPHY_PAGE_DATA, mask, set);
39970479a40SFrank }
40070479a40SFrank 
40170479a40SFrank /**
40270479a40SFrank  * ytphy_modify_ext_with_lock() - bits modify a PHY's extended register
40370479a40SFrank  * @phydev: a pointer to a &struct phy_device
40470479a40SFrank  * @regnum: register number to write
40570479a40SFrank  * @mask: bit mask of bits to clear
40670479a40SFrank  * @set: bit mask of bits to set
40770479a40SFrank  *
40870479a40SFrank  * NOTE: Convenience function which allows a PHY's extended register to be
40970479a40SFrank  * modified as new register value = (old register value & ~mask) | set.
41070479a40SFrank  *
41170479a40SFrank  * returns 0 or negative error code
41270479a40SFrank  */
ytphy_modify_ext_with_lock(struct phy_device * phydev,u16 regnum,u16 mask,u16 set)41370479a40SFrank static int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum,
41470479a40SFrank 				      u16 mask, u16 set)
41570479a40SFrank {
41670479a40SFrank 	int ret;
41770479a40SFrank 
41870479a40SFrank 	phy_lock_mdio_bus(phydev);
41970479a40SFrank 	ret = ytphy_modify_ext(phydev, regnum, mask, set);
42070479a40SFrank 	phy_unlock_mdio_bus(phydev);
42170479a40SFrank 
42270479a40SFrank 	return ret;
42370479a40SFrank }
42470479a40SFrank 
42570479a40SFrank /**
42670479a40SFrank  * ytphy_get_wol() - report whether wake-on-lan is enabled
42770479a40SFrank  * @phydev: a pointer to a &struct phy_device
42870479a40SFrank  * @wol: a pointer to a &struct ethtool_wolinfo
42970479a40SFrank  *
43070479a40SFrank  * NOTE: YTPHY_WOL_CONFIG_REG is common ext reg.
43170479a40SFrank  */
ytphy_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)43270479a40SFrank static void ytphy_get_wol(struct phy_device *phydev,
43370479a40SFrank 			  struct ethtool_wolinfo *wol)
43470479a40SFrank {
43570479a40SFrank 	int wol_config;
43670479a40SFrank 
43770479a40SFrank 	wol->supported = WAKE_MAGIC;
43870479a40SFrank 	wol->wolopts = 0;
43970479a40SFrank 
44070479a40SFrank 	wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
44170479a40SFrank 	if (wol_config < 0)
44270479a40SFrank 		return;
44370479a40SFrank 
44470479a40SFrank 	if (wol_config & YTPHY_WCR_ENABLE)
44570479a40SFrank 		wol->wolopts |= WAKE_MAGIC;
44670479a40SFrank }
44770479a40SFrank 
44870479a40SFrank /**
44970479a40SFrank  * ytphy_set_wol() - turn wake-on-lan on or off
45070479a40SFrank  * @phydev: a pointer to a &struct phy_device
45170479a40SFrank  * @wol: a pointer to a &struct ethtool_wolinfo
45270479a40SFrank  *
45370479a40SFrank  * NOTE: YTPHY_WOL_CONFIG_REG, YTPHY_WOL_MACADDR2_REG, YTPHY_WOL_MACADDR1_REG
45470479a40SFrank  * and YTPHY_WOL_MACADDR0_REG are common ext reg. The
45570479a40SFrank  * YTPHY_INTERRUPT_ENABLE_REG of UTP is special, fiber also use this register.
45670479a40SFrank  *
45770479a40SFrank  * returns 0 or negative errno code
45870479a40SFrank  */
ytphy_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)45970479a40SFrank static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
46070479a40SFrank {
46170479a40SFrank 	struct net_device *p_attached_dev;
46270479a40SFrank 	const u16 mac_addr_reg[] = {
46370479a40SFrank 		YTPHY_WOL_MACADDR2_REG,
46470479a40SFrank 		YTPHY_WOL_MACADDR1_REG,
46570479a40SFrank 		YTPHY_WOL_MACADDR0_REG,
46670479a40SFrank 	};
46770479a40SFrank 	const u8 *mac_addr;
46870479a40SFrank 	int old_page;
46970479a40SFrank 	int ret = 0;
47070479a40SFrank 	u16 mask;
47170479a40SFrank 	u16 val;
47270479a40SFrank 	u8 i;
47370479a40SFrank 
47470479a40SFrank 	if (wol->wolopts & WAKE_MAGIC) {
47570479a40SFrank 		p_attached_dev = phydev->attached_dev;
47670479a40SFrank 		if (!p_attached_dev)
47770479a40SFrank 			return -ENODEV;
47870479a40SFrank 
47970479a40SFrank 		mac_addr = (const u8 *)p_attached_dev->dev_addr;
48070479a40SFrank 		if (!is_valid_ether_addr(mac_addr))
48170479a40SFrank 			return -EINVAL;
48270479a40SFrank 
48370479a40SFrank 		/* lock mdio bus then switch to utp reg space */
48470479a40SFrank 		old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
48570479a40SFrank 		if (old_page < 0)
48670479a40SFrank 			goto err_restore_page;
48770479a40SFrank 
48870479a40SFrank 		/* Store the device address for the magic packet */
48970479a40SFrank 		for (i = 0; i < 3; i++) {
49070479a40SFrank 			ret = ytphy_write_ext(phydev, mac_addr_reg[i],
49170479a40SFrank 					      ((mac_addr[i * 2] << 8)) |
49270479a40SFrank 						      (mac_addr[i * 2 + 1]));
49370479a40SFrank 			if (ret < 0)
49470479a40SFrank 				goto err_restore_page;
49570479a40SFrank 		}
49670479a40SFrank 
49770479a40SFrank 		/* Enable WOL feature */
49870479a40SFrank 		mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
49970479a40SFrank 		val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
50070479a40SFrank 		val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
50170479a40SFrank 		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
50270479a40SFrank 		if (ret < 0)
50370479a40SFrank 			goto err_restore_page;
50470479a40SFrank 
50570479a40SFrank 		/* Enable WOL interrupt */
50670479a40SFrank 		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
50770479a40SFrank 				   YTPHY_IER_WOL);
50870479a40SFrank 		if (ret < 0)
50970479a40SFrank 			goto err_restore_page;
51070479a40SFrank 
51170479a40SFrank 	} else {
51270479a40SFrank 		old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
51370479a40SFrank 		if (old_page < 0)
51470479a40SFrank 			goto err_restore_page;
51570479a40SFrank 
51670479a40SFrank 		/* Disable WOL feature */
51770479a40SFrank 		mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
51870479a40SFrank 		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
51970479a40SFrank 
52070479a40SFrank 		/* Disable WOL interrupt */
52170479a40SFrank 		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
52270479a40SFrank 				   YTPHY_IER_WOL, 0);
52370479a40SFrank 		if (ret < 0)
52470479a40SFrank 			goto err_restore_page;
52570479a40SFrank 	}
52670479a40SFrank 
52770479a40SFrank err_restore_page:
52870479a40SFrank 	return phy_restore_page(phydev, old_page, ret);
52970479a40SFrank }
53070479a40SFrank 
yt8531_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)5314ac94f72SFrank Sae static int yt8531_set_wol(struct phy_device *phydev,
5324ac94f72SFrank Sae 			  struct ethtool_wolinfo *wol)
5334ac94f72SFrank Sae {
5344ac94f72SFrank Sae 	const u16 mac_addr_reg[] = {
5354ac94f72SFrank Sae 		YTPHY_WOL_MACADDR2_REG,
5364ac94f72SFrank Sae 		YTPHY_WOL_MACADDR1_REG,
5374ac94f72SFrank Sae 		YTPHY_WOL_MACADDR0_REG,
5384ac94f72SFrank Sae 	};
5394ac94f72SFrank Sae 	const u8 *mac_addr;
5404ac94f72SFrank Sae 	u16 mask, val;
5414ac94f72SFrank Sae 	int ret;
5424ac94f72SFrank Sae 	u8 i;
5434ac94f72SFrank Sae 
5444ac94f72SFrank Sae 	if (wol->wolopts & WAKE_MAGIC) {
5454ac94f72SFrank Sae 		mac_addr = phydev->attached_dev->dev_addr;
5464ac94f72SFrank Sae 
5474ac94f72SFrank Sae 		/* Store the device address for the magic packet */
5484ac94f72SFrank Sae 		for (i = 0; i < 3; i++) {
5494ac94f72SFrank Sae 			ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
5504ac94f72SFrank Sae 							((mac_addr[i * 2] << 8)) |
5514ac94f72SFrank Sae 							(mac_addr[i * 2 + 1]));
5524ac94f72SFrank Sae 			if (ret < 0)
5534ac94f72SFrank Sae 				return ret;
5544ac94f72SFrank Sae 		}
5554ac94f72SFrank Sae 
5564ac94f72SFrank Sae 		/* Enable WOL feature */
5574ac94f72SFrank Sae 		mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
5584ac94f72SFrank Sae 		val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
5594ac94f72SFrank Sae 		val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
5604ac94f72SFrank Sae 		ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
5614ac94f72SFrank Sae 						 mask, val);
5624ac94f72SFrank Sae 		if (ret < 0)
5634ac94f72SFrank Sae 			return ret;
5644ac94f72SFrank Sae 
5654ac94f72SFrank Sae 		/* Enable WOL interrupt */
5664ac94f72SFrank Sae 		ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
5674ac94f72SFrank Sae 				 YTPHY_IER_WOL);
5684ac94f72SFrank Sae 		if (ret < 0)
5694ac94f72SFrank Sae 			return ret;
5704ac94f72SFrank Sae 	} else {
5714ac94f72SFrank Sae 		/* Disable WOL feature */
5724ac94f72SFrank Sae 		mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
5734ac94f72SFrank Sae 		ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
5744ac94f72SFrank Sae 						 mask, 0);
5754ac94f72SFrank Sae 
5764ac94f72SFrank Sae 		/* Disable WOL interrupt */
5774ac94f72SFrank Sae 		ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
5784ac94f72SFrank Sae 				 YTPHY_IER_WOL, 0);
5794ac94f72SFrank Sae 		if (ret < 0)
5804ac94f72SFrank Sae 			return ret;
5814ac94f72SFrank Sae 	}
5824ac94f72SFrank Sae 
5834ac94f72SFrank Sae 	return 0;
5844ac94f72SFrank Sae }
5854ac94f72SFrank Sae 
yt8511_read_page(struct phy_device * phydev)58648e8c6f1SPeter Geis static int yt8511_read_page(struct phy_device *phydev)
58748e8c6f1SPeter Geis {
58848e8c6f1SPeter Geis 	return __phy_read(phydev, YT8511_PAGE_SELECT);
58948e8c6f1SPeter Geis };
59048e8c6f1SPeter Geis 
yt8511_write_page(struct phy_device * phydev,int page)59148e8c6f1SPeter Geis static int yt8511_write_page(struct phy_device *phydev, int page)
59248e8c6f1SPeter Geis {
59348e8c6f1SPeter Geis 	return __phy_write(phydev, YT8511_PAGE_SELECT, page);
59448e8c6f1SPeter Geis };
59548e8c6f1SPeter Geis 
yt8511_config_init(struct phy_device * phydev)59648e8c6f1SPeter Geis static int yt8511_config_init(struct phy_device *phydev)
59748e8c6f1SPeter Geis {
598546d6badSPeter Geis 	int oldpage, ret = 0;
59948e8c6f1SPeter Geis 	unsigned int ge, fe;
60048e8c6f1SPeter Geis 
60148e8c6f1SPeter Geis 	oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE);
60248e8c6f1SPeter Geis 	if (oldpage < 0)
60348e8c6f1SPeter Geis 		goto err_restore_page;
60448e8c6f1SPeter Geis 
60548e8c6f1SPeter Geis 	/* set rgmii delay mode */
60648e8c6f1SPeter Geis 	switch (phydev->interface) {
60748e8c6f1SPeter Geis 	case PHY_INTERFACE_MODE_RGMII:
60848e8c6f1SPeter Geis 		ge = YT8511_DELAY_GE_TX_DIS;
60948e8c6f1SPeter Geis 		fe = YT8511_DELAY_FE_TX_DIS;
61048e8c6f1SPeter Geis 		break;
61148e8c6f1SPeter Geis 	case PHY_INTERFACE_MODE_RGMII_RXID:
61248e8c6f1SPeter Geis 		ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS;
61348e8c6f1SPeter Geis 		fe = YT8511_DELAY_FE_TX_DIS;
61448e8c6f1SPeter Geis 		break;
61548e8c6f1SPeter Geis 	case PHY_INTERFACE_MODE_RGMII_TXID:
61648e8c6f1SPeter Geis 		ge = YT8511_DELAY_GE_TX_EN;
61748e8c6f1SPeter Geis 		fe = YT8511_DELAY_FE_TX_EN;
61848e8c6f1SPeter Geis 		break;
61948e8c6f1SPeter Geis 	case PHY_INTERFACE_MODE_RGMII_ID:
62048e8c6f1SPeter Geis 		ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN;
62148e8c6f1SPeter Geis 		fe = YT8511_DELAY_FE_TX_EN;
62248e8c6f1SPeter Geis 		break;
6230cc8bddbSPeter Geis 	default: /* do not support other modes */
6240cc8bddbSPeter Geis 		ret = -EOPNOTSUPP;
6250cc8bddbSPeter Geis 		goto err_restore_page;
62648e8c6f1SPeter Geis 	}
62748e8c6f1SPeter Geis 
62848e8c6f1SPeter Geis 	ret = __phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge);
62948e8c6f1SPeter Geis 	if (ret < 0)
63048e8c6f1SPeter Geis 		goto err_restore_page;
63148e8c6f1SPeter Geis 
6320cc8bddbSPeter Geis 	/* set clock mode to 125mhz */
6330cc8bddbSPeter Geis 	ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M);
6340cc8bddbSPeter Geis 	if (ret < 0)
6350cc8bddbSPeter Geis 		goto err_restore_page;
6360cc8bddbSPeter Geis 
63748e8c6f1SPeter Geis 	/* fast ethernet delay is in a separate page */
63848e8c6f1SPeter Geis 	ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE);
63948e8c6f1SPeter Geis 	if (ret < 0)
64048e8c6f1SPeter Geis 		goto err_restore_page;
64148e8c6f1SPeter Geis 
64248e8c6f1SPeter Geis 	ret = __phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe);
64348e8c6f1SPeter Geis 	if (ret < 0)
64448e8c6f1SPeter Geis 		goto err_restore_page;
64548e8c6f1SPeter Geis 
64648e8c6f1SPeter Geis 	/* leave pll enabled in sleep */
64748e8c6f1SPeter Geis 	ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL);
64848e8c6f1SPeter Geis 	if (ret < 0)
64948e8c6f1SPeter Geis 		goto err_restore_page;
65048e8c6f1SPeter Geis 
65148e8c6f1SPeter Geis 	ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP);
65248e8c6f1SPeter Geis 	if (ret < 0)
65348e8c6f1SPeter Geis 		goto err_restore_page;
65448e8c6f1SPeter Geis 
65548e8c6f1SPeter Geis err_restore_page:
65648e8c6f1SPeter Geis 	return phy_restore_page(phydev, oldpage, ret);
65748e8c6f1SPeter Geis }
65848e8c6f1SPeter Geis 
65970479a40SFrank /**
66070479a40SFrank  * yt8521_read_page() - read reg page
66170479a40SFrank  * @phydev: a pointer to a &struct phy_device
66270479a40SFrank  *
66370479a40SFrank  * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE/
66470479a40SFrank  * YT8521_RSSR_UTP_SPACE) or negative errno code
66570479a40SFrank  */
yt8521_read_page(struct phy_device * phydev)66670479a40SFrank static int yt8521_read_page(struct phy_device *phydev)
66770479a40SFrank {
66870479a40SFrank 	int old_page;
66970479a40SFrank 
67070479a40SFrank 	old_page = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
67170479a40SFrank 	if (old_page < 0)
67270479a40SFrank 		return old_page;
67370479a40SFrank 
67470479a40SFrank 	if ((old_page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
67570479a40SFrank 		return YT8521_RSSR_FIBER_SPACE;
67670479a40SFrank 
67770479a40SFrank 	return YT8521_RSSR_UTP_SPACE;
67870479a40SFrank };
67970479a40SFrank 
68070479a40SFrank /**
68170479a40SFrank  * yt8521_write_page() - write reg page
68270479a40SFrank  * @phydev: a pointer to a &struct phy_device
68370479a40SFrank  * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write.
68470479a40SFrank  *
68570479a40SFrank  * returns 0 or negative errno code
68670479a40SFrank  */
yt8521_write_page(struct phy_device * phydev,int page)68770479a40SFrank static int yt8521_write_page(struct phy_device *phydev, int page)
68870479a40SFrank {
68970479a40SFrank 	int mask = YT8521_RSSR_SPACE_MASK;
69070479a40SFrank 	int set;
69170479a40SFrank 
69270479a40SFrank 	if ((page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
69370479a40SFrank 		set = YT8521_RSSR_FIBER_SPACE;
69470479a40SFrank 	else
69570479a40SFrank 		set = YT8521_RSSR_UTP_SPACE;
69670479a40SFrank 
69770479a40SFrank 	return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set);
69870479a40SFrank };
69970479a40SFrank 
70070479a40SFrank /**
701a6e68f0fSFrank Sae  * struct ytphy_cfg_reg_map - map a config value to a register value
702a6e68f0fSFrank Sae  * @cfg: value in device configuration
703a6e68f0fSFrank Sae  * @reg: value in the register
704a6e68f0fSFrank Sae  */
705a6e68f0fSFrank Sae struct ytphy_cfg_reg_map {
706a6e68f0fSFrank Sae 	u32 cfg;
707a6e68f0fSFrank Sae 	u32 reg;
708a6e68f0fSFrank Sae };
709a6e68f0fSFrank Sae 
710a6e68f0fSFrank Sae static const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = {
711a6e68f0fSFrank Sae 	/* for tx delay / rx delay with YT8521_CCR_RXC_DLY_EN is not set. */
712a6e68f0fSFrank Sae 	{ 0,	YT8521_RC1R_RGMII_0_000_NS },
713a6e68f0fSFrank Sae 	{ 150,	YT8521_RC1R_RGMII_0_150_NS },
714a6e68f0fSFrank Sae 	{ 300,	YT8521_RC1R_RGMII_0_300_NS },
715a6e68f0fSFrank Sae 	{ 450,	YT8521_RC1R_RGMII_0_450_NS },
716a6e68f0fSFrank Sae 	{ 600,	YT8521_RC1R_RGMII_0_600_NS },
717a6e68f0fSFrank Sae 	{ 750,	YT8521_RC1R_RGMII_0_750_NS },
718a6e68f0fSFrank Sae 	{ 900,	YT8521_RC1R_RGMII_0_900_NS },
719a6e68f0fSFrank Sae 	{ 1050,	YT8521_RC1R_RGMII_1_050_NS },
720a6e68f0fSFrank Sae 	{ 1200,	YT8521_RC1R_RGMII_1_200_NS },
721a6e68f0fSFrank Sae 	{ 1350,	YT8521_RC1R_RGMII_1_350_NS },
722a6e68f0fSFrank Sae 	{ 1500,	YT8521_RC1R_RGMII_1_500_NS },
723a6e68f0fSFrank Sae 	{ 1650,	YT8521_RC1R_RGMII_1_650_NS },
724a6e68f0fSFrank Sae 	{ 1800,	YT8521_RC1R_RGMII_1_800_NS },
725a6e68f0fSFrank Sae 	{ 1950,	YT8521_RC1R_RGMII_1_950_NS },	/* default tx/rx delay */
726a6e68f0fSFrank Sae 	{ 2100,	YT8521_RC1R_RGMII_2_100_NS },
727a6e68f0fSFrank Sae 	{ 2250,	YT8521_RC1R_RGMII_2_250_NS },
728a6e68f0fSFrank Sae 
729a6e68f0fSFrank Sae 	/* only for rx delay with YT8521_CCR_RXC_DLY_EN is set. */
730a6e68f0fSFrank Sae 	{ 0    + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_000_NS },
731a6e68f0fSFrank Sae 	{ 150  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_150_NS },
732a6e68f0fSFrank Sae 	{ 300  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_300_NS },
733a6e68f0fSFrank Sae 	{ 450  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_450_NS },
734a6e68f0fSFrank Sae 	{ 600  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_600_NS },
735a6e68f0fSFrank Sae 	{ 750  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_750_NS },
736a6e68f0fSFrank Sae 	{ 900  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_900_NS },
737a6e68f0fSFrank Sae 	{ 1050 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_050_NS },
738a6e68f0fSFrank Sae 	{ 1200 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_200_NS },
739a6e68f0fSFrank Sae 	{ 1350 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_350_NS },
740a6e68f0fSFrank Sae 	{ 1500 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_500_NS },
741a6e68f0fSFrank Sae 	{ 1650 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_650_NS },
742a6e68f0fSFrank Sae 	{ 1800 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_800_NS },
743a6e68f0fSFrank Sae 	{ 1950 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_950_NS },
744a6e68f0fSFrank Sae 	{ 2100 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_2_100_NS },
745a6e68f0fSFrank Sae 	{ 2250 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_2_250_NS }
746a6e68f0fSFrank Sae };
747a6e68f0fSFrank Sae 
ytphy_get_delay_reg_value(struct phy_device * phydev,const char * prop_name,const struct ytphy_cfg_reg_map * tbl,int tb_size,u16 * rxc_dly_en,u32 dflt)748a6e68f0fSFrank Sae static u32 ytphy_get_delay_reg_value(struct phy_device *phydev,
749a6e68f0fSFrank Sae 				     const char *prop_name,
750a6e68f0fSFrank Sae 				     const struct ytphy_cfg_reg_map *tbl,
751a6e68f0fSFrank Sae 				     int tb_size,
752a6e68f0fSFrank Sae 				     u16 *rxc_dly_en,
753a6e68f0fSFrank Sae 				     u32 dflt)
754a6e68f0fSFrank Sae {
755a6e68f0fSFrank Sae 	struct device_node *node = phydev->mdio.dev.of_node;
756a6e68f0fSFrank Sae 	int tb_size_half = tb_size / 2;
757a6e68f0fSFrank Sae 	u32 val;
758a6e68f0fSFrank Sae 	int i;
759a6e68f0fSFrank Sae 
760a6e68f0fSFrank Sae 	if (of_property_read_u32(node, prop_name, &val))
761a6e68f0fSFrank Sae 		goto err_dts_val;
762a6e68f0fSFrank Sae 
763a6e68f0fSFrank Sae 	/* when rxc_dly_en is NULL, it is get the delay for tx, only half of
764a6e68f0fSFrank Sae 	 * tb_size is valid.
765a6e68f0fSFrank Sae 	 */
766a6e68f0fSFrank Sae 	if (!rxc_dly_en)
767a6e68f0fSFrank Sae 		tb_size = tb_size_half;
768a6e68f0fSFrank Sae 
769a6e68f0fSFrank Sae 	for (i = 0; i < tb_size; i++) {
770a6e68f0fSFrank Sae 		if (tbl[i].cfg == val) {
771a6e68f0fSFrank Sae 			if (rxc_dly_en && i < tb_size_half)
772a6e68f0fSFrank Sae 				*rxc_dly_en = 0;
773a6e68f0fSFrank Sae 			return tbl[i].reg;
774a6e68f0fSFrank Sae 		}
775a6e68f0fSFrank Sae 	}
776a6e68f0fSFrank Sae 
777a6e68f0fSFrank Sae 	phydev_warn(phydev, "Unsupported value %d for %s using default (%u)\n",
778a6e68f0fSFrank Sae 		    val, prop_name, dflt);
779a6e68f0fSFrank Sae 
780a6e68f0fSFrank Sae err_dts_val:
781a6e68f0fSFrank Sae 	/* when rxc_dly_en is not NULL, it is get the delay for rx.
782a6e68f0fSFrank Sae 	 * The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps,
783a6e68f0fSFrank Sae 	 * so YT8521_CCR_RXC_DLY_EN should not be set.
784a6e68f0fSFrank Sae 	 */
785a6e68f0fSFrank Sae 	if (rxc_dly_en)
786a6e68f0fSFrank Sae 		*rxc_dly_en = 0;
787a6e68f0fSFrank Sae 
788a6e68f0fSFrank Sae 	return dflt;
789a6e68f0fSFrank Sae }
790a6e68f0fSFrank Sae 
ytphy_rgmii_clk_delay_config(struct phy_device * phydev)791a6e68f0fSFrank Sae static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
792a6e68f0fSFrank Sae {
793a6e68f0fSFrank Sae 	int tb_size = ARRAY_SIZE(ytphy_rgmii_delays);
794a6e68f0fSFrank Sae 	u16 rxc_dly_en = YT8521_CCR_RXC_DLY_EN;
795a6e68f0fSFrank Sae 	u32 rx_reg, tx_reg;
796a6e68f0fSFrank Sae 	u16 mask, val = 0;
797a6e68f0fSFrank Sae 	int ret;
798a6e68f0fSFrank Sae 
799a6e68f0fSFrank Sae 	rx_reg = ytphy_get_delay_reg_value(phydev, "rx-internal-delay-ps",
800a6e68f0fSFrank Sae 					   ytphy_rgmii_delays, tb_size,
801a6e68f0fSFrank Sae 					   &rxc_dly_en,
802a6e68f0fSFrank Sae 					   YT8521_RC1R_RGMII_1_950_NS);
803a6e68f0fSFrank Sae 	tx_reg = ytphy_get_delay_reg_value(phydev, "tx-internal-delay-ps",
804a6e68f0fSFrank Sae 					   ytphy_rgmii_delays, tb_size, NULL,
805a6e68f0fSFrank Sae 					   YT8521_RC1R_RGMII_1_950_NS);
806a6e68f0fSFrank Sae 
807a6e68f0fSFrank Sae 	switch (phydev->interface) {
808a6e68f0fSFrank Sae 	case PHY_INTERFACE_MODE_RGMII:
809a6e68f0fSFrank Sae 		rxc_dly_en = 0;
810a6e68f0fSFrank Sae 		break;
811a6e68f0fSFrank Sae 	case PHY_INTERFACE_MODE_RGMII_RXID:
812a6e68f0fSFrank Sae 		val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg);
813a6e68f0fSFrank Sae 		break;
814a6e68f0fSFrank Sae 	case PHY_INTERFACE_MODE_RGMII_TXID:
815a6e68f0fSFrank Sae 		rxc_dly_en = 0;
816a6e68f0fSFrank Sae 		val |= FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
817a6e68f0fSFrank Sae 		break;
818a6e68f0fSFrank Sae 	case PHY_INTERFACE_MODE_RGMII_ID:
819a6e68f0fSFrank Sae 		val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) |
820a6e68f0fSFrank Sae 		       FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
821a6e68f0fSFrank Sae 		break;
822a6e68f0fSFrank Sae 	default: /* do not support other modes */
823a6e68f0fSFrank Sae 		return -EOPNOTSUPP;
824a6e68f0fSFrank Sae 	}
825a6e68f0fSFrank Sae 
826a6e68f0fSFrank Sae 	ret = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
827a6e68f0fSFrank Sae 			       YT8521_CCR_RXC_DLY_EN, rxc_dly_en);
828a6e68f0fSFrank Sae 	if (ret < 0)
829a6e68f0fSFrank Sae 		return ret;
830a6e68f0fSFrank Sae 
831a6e68f0fSFrank Sae 	/* Generally, it is not necessary to adjust YT8521_RC1R_FE_TX_DELAY */
832a6e68f0fSFrank Sae 	mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_GE_TX_DELAY_MASK;
833a6e68f0fSFrank Sae 	return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
834a6e68f0fSFrank Sae }
835a6e68f0fSFrank Sae 
ytphy_rgmii_clk_delay_config_with_lock(struct phy_device * phydev)8364ac94f72SFrank Sae static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
8374ac94f72SFrank Sae {
8384ac94f72SFrank Sae 	int ret;
8394ac94f72SFrank Sae 
8404ac94f72SFrank Sae 	phy_lock_mdio_bus(phydev);
8414ac94f72SFrank Sae 	ret = ytphy_rgmii_clk_delay_config(phydev);
8424ac94f72SFrank Sae 	phy_unlock_mdio_bus(phydev);
8434ac94f72SFrank Sae 
8444ac94f72SFrank Sae 	return ret;
8454ac94f72SFrank Sae }
8464ac94f72SFrank Sae 
847a6e68f0fSFrank Sae /**
848*7a561e93SSamin Guo  * struct ytphy_ldo_vol_map - map a current value to a register value
849*7a561e93SSamin Guo  * @vol: ldo voltage
850*7a561e93SSamin Guo  * @ds:  value in the register
851*7a561e93SSamin Guo  * @cur: value in device configuration
852*7a561e93SSamin Guo  */
853*7a561e93SSamin Guo struct ytphy_ldo_vol_map {
854*7a561e93SSamin Guo 	u32 vol;
855*7a561e93SSamin Guo 	u32 ds;
856*7a561e93SSamin Guo 	u32 cur;
857*7a561e93SSamin Guo };
858*7a561e93SSamin Guo 
859*7a561e93SSamin Guo static const struct ytphy_ldo_vol_map yt8531_ldo_vol[] = {
860*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 0, .cur = 1200},
861*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 1, .cur = 2100},
862*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 2, .cur = 2700},
863*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 3, .cur = 2910},
864*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 4, .cur = 3110},
865*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 5, .cur = 3600},
866*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 6, .cur = 3970},
867*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_1V8, .ds = 7, .cur = 4350},
868*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 0, .cur = 3070},
869*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 1, .cur = 4080},
870*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 2, .cur = 4370},
871*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 3, .cur = 4680},
872*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 4, .cur = 5020},
873*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 5, .cur = 5450},
874*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 6, .cur = 5740},
875*7a561e93SSamin Guo 	{.vol = YT8531_LDO_VOL_3V3, .ds = 7, .cur = 6140},
876*7a561e93SSamin Guo };
877*7a561e93SSamin Guo 
yt8531_get_ldo_vol(struct phy_device * phydev)878*7a561e93SSamin Guo static u32 yt8531_get_ldo_vol(struct phy_device *phydev)
879*7a561e93SSamin Guo {
880*7a561e93SSamin Guo 	u32 val;
881*7a561e93SSamin Guo 
882*7a561e93SSamin Guo 	val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
883*7a561e93SSamin Guo 	val = FIELD_GET(YT8531_RGMII_LDO_VOL_MASK, val);
884*7a561e93SSamin Guo 
885*7a561e93SSamin Guo 	return val <= YT8531_LDO_VOL_1V8 ? val : YT8531_LDO_VOL_1V8;
886*7a561e93SSamin Guo }
887*7a561e93SSamin Guo 
yt8531_get_ds_map(struct phy_device * phydev,u32 cur)888*7a561e93SSamin Guo static int yt8531_get_ds_map(struct phy_device *phydev, u32 cur)
889*7a561e93SSamin Guo {
890*7a561e93SSamin Guo 	u32 vol;
891*7a561e93SSamin Guo 	int i;
892*7a561e93SSamin Guo 
893*7a561e93SSamin Guo 	vol = yt8531_get_ldo_vol(phydev);
894*7a561e93SSamin Guo 	for (i = 0; i < ARRAY_SIZE(yt8531_ldo_vol); i++) {
895*7a561e93SSamin Guo 		if (yt8531_ldo_vol[i].vol == vol && yt8531_ldo_vol[i].cur == cur)
896*7a561e93SSamin Guo 			return yt8531_ldo_vol[i].ds;
897*7a561e93SSamin Guo 	}
898*7a561e93SSamin Guo 
899*7a561e93SSamin Guo 	return -EINVAL;
900*7a561e93SSamin Guo }
901*7a561e93SSamin Guo 
yt8531_set_ds(struct phy_device * phydev)902*7a561e93SSamin Guo static int yt8531_set_ds(struct phy_device *phydev)
903*7a561e93SSamin Guo {
904*7a561e93SSamin Guo 	struct device_node *node = phydev->mdio.dev.of_node;
905*7a561e93SSamin Guo 	u32 ds_field_low, ds_field_hi, val;
906*7a561e93SSamin Guo 	int ret, ds;
907*7a561e93SSamin Guo 
908*7a561e93SSamin Guo 	/* set rgmii rx clk driver strength */
909*7a561e93SSamin Guo 	if (!of_property_read_u32(node, "motorcomm,rx-clk-drv-microamp", &val)) {
910*7a561e93SSamin Guo 		ds = yt8531_get_ds_map(phydev, val);
911*7a561e93SSamin Guo 		if (ds < 0)
912*7a561e93SSamin Guo 			return dev_err_probe(&phydev->mdio.dev, ds,
913*7a561e93SSamin Guo 					     "No matching current value was found.\n");
914*7a561e93SSamin Guo 	} else {
915*7a561e93SSamin Guo 		ds = YT8531_RGMII_RX_DS_DEFAULT;
916*7a561e93SSamin Guo 	}
917*7a561e93SSamin Guo 
918*7a561e93SSamin Guo 	ret = ytphy_modify_ext_with_lock(phydev,
919*7a561e93SSamin Guo 					 YTPHY_PAD_DRIVE_STRENGTH_REG,
920*7a561e93SSamin Guo 					 YT8531_RGMII_RXC_DS_MASK,
921*7a561e93SSamin Guo 					 FIELD_PREP(YT8531_RGMII_RXC_DS_MASK, ds));
922*7a561e93SSamin Guo 	if (ret < 0)
923*7a561e93SSamin Guo 		return ret;
924*7a561e93SSamin Guo 
925*7a561e93SSamin Guo 	/* set rgmii rx data driver strength */
926*7a561e93SSamin Guo 	if (!of_property_read_u32(node, "motorcomm,rx-data-drv-microamp", &val)) {
927*7a561e93SSamin Guo 		ds = yt8531_get_ds_map(phydev, val);
928*7a561e93SSamin Guo 		if (ds < 0)
929*7a561e93SSamin Guo 			return dev_err_probe(&phydev->mdio.dev, ds,
930*7a561e93SSamin Guo 					     "No matching current value was found.\n");
931*7a561e93SSamin Guo 	} else {
932*7a561e93SSamin Guo 		ds = YT8531_RGMII_RX_DS_DEFAULT;
933*7a561e93SSamin Guo 	}
934*7a561e93SSamin Guo 
935*7a561e93SSamin Guo 	ds_field_hi = FIELD_GET(BIT(2), ds);
936*7a561e93SSamin Guo 	ds_field_hi = FIELD_PREP(YT8531_RGMII_RXD_DS_HI_MASK, ds_field_hi);
937*7a561e93SSamin Guo 
938*7a561e93SSamin Guo 	ds_field_low = FIELD_GET(GENMASK(1, 0), ds);
939*7a561e93SSamin Guo 	ds_field_low = FIELD_PREP(YT8531_RGMII_RXD_DS_LOW_MASK, ds_field_low);
940*7a561e93SSamin Guo 
941*7a561e93SSamin Guo 	ret = ytphy_modify_ext_with_lock(phydev,
942*7a561e93SSamin Guo 					 YTPHY_PAD_DRIVE_STRENGTH_REG,
943*7a561e93SSamin Guo 					 YT8531_RGMII_RXD_DS_LOW_MASK | YT8531_RGMII_RXD_DS_HI_MASK,
944*7a561e93SSamin Guo 					 ds_field_low | ds_field_hi);
945*7a561e93SSamin Guo 	if (ret < 0)
946*7a561e93SSamin Guo 		return ret;
947*7a561e93SSamin Guo 
948*7a561e93SSamin Guo 	return 0;
949*7a561e93SSamin Guo }
950*7a561e93SSamin Guo 
951*7a561e93SSamin Guo /**
95270479a40SFrank  * yt8521_probe() - read chip config then set suitable polling_mode
95370479a40SFrank  * @phydev: a pointer to a &struct phy_device
95470479a40SFrank  *
95570479a40SFrank  * returns 0 or negative errno code
95670479a40SFrank  */
yt8521_probe(struct phy_device * phydev)95770479a40SFrank static int yt8521_probe(struct phy_device *phydev)
95870479a40SFrank {
959a6e68f0fSFrank Sae 	struct device_node *node = phydev->mdio.dev.of_node;
96070479a40SFrank 	struct device *dev = &phydev->mdio.dev;
96170479a40SFrank 	struct yt8521_priv *priv;
96270479a40SFrank 	int chip_config;
963a6e68f0fSFrank Sae 	u16 mask, val;
964a6e68f0fSFrank Sae 	u32 freq;
96570479a40SFrank 	int ret;
96670479a40SFrank 
96770479a40SFrank 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
96870479a40SFrank 	if (!priv)
96970479a40SFrank 		return -ENOMEM;
97070479a40SFrank 
97170479a40SFrank 	phydev->priv = priv;
97270479a40SFrank 
97370479a40SFrank 	chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
97470479a40SFrank 	if (chip_config < 0)
97570479a40SFrank 		return chip_config;
97670479a40SFrank 
97770479a40SFrank 	priv->strap_mode = chip_config & YT8521_CCR_MODE_SEL_MASK;
97870479a40SFrank 	switch (priv->strap_mode) {
97970479a40SFrank 	case YT8521_CCR_MODE_FIBER_TO_RGMII:
98070479a40SFrank 	case YT8521_CCR_MODE_SGPHY_TO_RGMAC:
98170479a40SFrank 	case YT8521_CCR_MODE_SGMAC_TO_RGPHY:
98270479a40SFrank 		priv->polling_mode = YT8521_MODE_FIBER;
98370479a40SFrank 		priv->reg_page = YT8521_RSSR_FIBER_SPACE;
98470479a40SFrank 		phydev->port = PORT_FIBRE;
98570479a40SFrank 		break;
98670479a40SFrank 	case YT8521_CCR_MODE_UTP_FIBER_TO_RGMII:
98770479a40SFrank 	case YT8521_CCR_MODE_UTP_TO_FIBER_AUTO:
98870479a40SFrank 	case YT8521_CCR_MODE_UTP_TO_FIBER_FORCE:
98970479a40SFrank 		priv->polling_mode = YT8521_MODE_POLL;
99070479a40SFrank 		priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
99170479a40SFrank 		phydev->port = PORT_NONE;
99270479a40SFrank 		break;
99370479a40SFrank 	case YT8521_CCR_MODE_UTP_TO_SGMII:
99470479a40SFrank 	case YT8521_CCR_MODE_UTP_TO_RGMII:
99570479a40SFrank 		priv->polling_mode = YT8521_MODE_UTP;
99670479a40SFrank 		priv->reg_page = YT8521_RSSR_UTP_SPACE;
99770479a40SFrank 		phydev->port = PORT_TP;
99870479a40SFrank 		break;
99970479a40SFrank 	}
100070479a40SFrank 	/* set default reg space */
100170479a40SFrank 	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
100270479a40SFrank 		ret = ytphy_write_ext_with_lock(phydev,
100370479a40SFrank 						YT8521_REG_SPACE_SELECT_REG,
100470479a40SFrank 						priv->reg_page);
100570479a40SFrank 		if (ret < 0)
100670479a40SFrank 			return ret;
100770479a40SFrank 	}
100870479a40SFrank 
1009a6e68f0fSFrank Sae 	if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
1010a6e68f0fSFrank Sae 		freq = YTPHY_DTS_OUTPUT_CLK_DIS;
1011a6e68f0fSFrank Sae 
1012a6e68f0fSFrank Sae 	if (phydev->drv->phy_id == PHY_ID_YT8521) {
1013a6e68f0fSFrank Sae 		switch (freq) {
1014a6e68f0fSFrank Sae 		case YTPHY_DTS_OUTPUT_CLK_DIS:
1015a6e68f0fSFrank Sae 			mask = YT8521_SCR_SYNCE_ENABLE;
1016a6e68f0fSFrank Sae 			val = 0;
1017a6e68f0fSFrank Sae 			break;
1018a6e68f0fSFrank Sae 		case YTPHY_DTS_OUTPUT_CLK_25M:
1019a6e68f0fSFrank Sae 			mask = YT8521_SCR_SYNCE_ENABLE |
1020a6e68f0fSFrank Sae 			       YT8521_SCR_CLK_SRC_MASK |
1021a6e68f0fSFrank Sae 			       YT8521_SCR_CLK_FRE_SEL_125M;
1022a6e68f0fSFrank Sae 			val = YT8521_SCR_SYNCE_ENABLE |
1023a6e68f0fSFrank Sae 			      FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
1024a6e68f0fSFrank Sae 					 YT8521_SCR_CLK_SRC_REF_25M);
1025a6e68f0fSFrank Sae 			break;
1026a6e68f0fSFrank Sae 		case YTPHY_DTS_OUTPUT_CLK_125M:
1027a6e68f0fSFrank Sae 			mask = YT8521_SCR_SYNCE_ENABLE |
1028a6e68f0fSFrank Sae 			       YT8521_SCR_CLK_SRC_MASK |
1029a6e68f0fSFrank Sae 			       YT8521_SCR_CLK_FRE_SEL_125M;
1030a6e68f0fSFrank Sae 			val = YT8521_SCR_SYNCE_ENABLE |
1031a6e68f0fSFrank Sae 			      YT8521_SCR_CLK_FRE_SEL_125M |
1032a6e68f0fSFrank Sae 			      FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
1033a6e68f0fSFrank Sae 					 YT8521_SCR_CLK_SRC_PLL_125M);
1034a6e68f0fSFrank Sae 			break;
1035a6e68f0fSFrank Sae 		default:
1036a6e68f0fSFrank Sae 			phydev_warn(phydev, "Freq err:%u\n", freq);
1037a6e68f0fSFrank Sae 			return -EINVAL;
1038a6e68f0fSFrank Sae 		}
1039a6e68f0fSFrank Sae 	} else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
104036152f87SFrank Sae 		switch (freq) {
104136152f87SFrank Sae 		case YTPHY_DTS_OUTPUT_CLK_DIS:
104236152f87SFrank Sae 			mask = YT8531_SCR_SYNCE_ENABLE;
104336152f87SFrank Sae 			val = 0;
104436152f87SFrank Sae 			break;
104536152f87SFrank Sae 		case YTPHY_DTS_OUTPUT_CLK_25M:
104636152f87SFrank Sae 			mask = YT8531_SCR_SYNCE_ENABLE |
104736152f87SFrank Sae 			       YT8531_SCR_CLK_SRC_MASK |
104836152f87SFrank Sae 			       YT8531_SCR_CLK_FRE_SEL_125M;
104936152f87SFrank Sae 			val = YT8531_SCR_SYNCE_ENABLE |
105036152f87SFrank Sae 			      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
105136152f87SFrank Sae 					 YT8531_SCR_CLK_SRC_REF_25M);
105236152f87SFrank Sae 			break;
105336152f87SFrank Sae 		case YTPHY_DTS_OUTPUT_CLK_125M:
105436152f87SFrank Sae 			mask = YT8531_SCR_SYNCE_ENABLE |
105536152f87SFrank Sae 			       YT8531_SCR_CLK_SRC_MASK |
105636152f87SFrank Sae 			       YT8531_SCR_CLK_FRE_SEL_125M;
105736152f87SFrank Sae 			val = YT8531_SCR_SYNCE_ENABLE |
105836152f87SFrank Sae 			      YT8531_SCR_CLK_FRE_SEL_125M |
105936152f87SFrank Sae 			      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
106036152f87SFrank Sae 					 YT8531_SCR_CLK_SRC_PLL_125M);
106136152f87SFrank Sae 			break;
106236152f87SFrank Sae 		default:
106336152f87SFrank Sae 			phydev_warn(phydev, "Freq err:%u\n", freq);
106436152f87SFrank Sae 			return -EINVAL;
106536152f87SFrank Sae 		}
1066a6e68f0fSFrank Sae 	} else {
1067a6e68f0fSFrank Sae 		phydev_warn(phydev, "PHY id err\n");
1068a6e68f0fSFrank Sae 		return -EINVAL;
1069a6e68f0fSFrank Sae 	}
1070a6e68f0fSFrank Sae 
1071a6e68f0fSFrank Sae 	return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
1072a6e68f0fSFrank Sae 					  val);
107370479a40SFrank }
107470479a40SFrank 
yt8531_probe(struct phy_device * phydev)10754ac94f72SFrank Sae static int yt8531_probe(struct phy_device *phydev)
10764ac94f72SFrank Sae {
10774ac94f72SFrank Sae 	struct device_node *node = phydev->mdio.dev.of_node;
10784ac94f72SFrank Sae 	u16 mask, val;
10794ac94f72SFrank Sae 	u32 freq;
10804ac94f72SFrank Sae 
10814ac94f72SFrank Sae 	if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
10824ac94f72SFrank Sae 		freq = YTPHY_DTS_OUTPUT_CLK_DIS;
10834ac94f72SFrank Sae 
10844ac94f72SFrank Sae 	switch (freq) {
10854ac94f72SFrank Sae 	case YTPHY_DTS_OUTPUT_CLK_DIS:
10864ac94f72SFrank Sae 		mask = YT8531_SCR_SYNCE_ENABLE;
10874ac94f72SFrank Sae 		val = 0;
10884ac94f72SFrank Sae 		break;
10894ac94f72SFrank Sae 	case YTPHY_DTS_OUTPUT_CLK_25M:
10904ac94f72SFrank Sae 		mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
10914ac94f72SFrank Sae 		       YT8531_SCR_CLK_FRE_SEL_125M;
10924ac94f72SFrank Sae 		val = YT8531_SCR_SYNCE_ENABLE |
10934ac94f72SFrank Sae 		      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
10944ac94f72SFrank Sae 				 YT8531_SCR_CLK_SRC_REF_25M);
10954ac94f72SFrank Sae 		break;
10964ac94f72SFrank Sae 	case YTPHY_DTS_OUTPUT_CLK_125M:
10974ac94f72SFrank Sae 		mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
10984ac94f72SFrank Sae 		       YT8531_SCR_CLK_FRE_SEL_125M;
10994ac94f72SFrank Sae 		val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
11004ac94f72SFrank Sae 		      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
11014ac94f72SFrank Sae 				 YT8531_SCR_CLK_SRC_PLL_125M);
11024ac94f72SFrank Sae 		break;
11034ac94f72SFrank Sae 	default:
11044ac94f72SFrank Sae 		phydev_warn(phydev, "Freq err:%u\n", freq);
11054ac94f72SFrank Sae 		return -EINVAL;
11064ac94f72SFrank Sae 	}
11074ac94f72SFrank Sae 
11084ac94f72SFrank Sae 	return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
11094ac94f72SFrank Sae 					  val);
11104ac94f72SFrank Sae }
11114ac94f72SFrank Sae 
111270479a40SFrank /**
111370479a40SFrank  * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
111470479a40SFrank  * @phydev: a pointer to a &struct phy_device
111570479a40SFrank  *
111670479a40SFrank  * NOTE:The caller must have taken the MDIO bus lock.
111770479a40SFrank  *
111870479a40SFrank  * returns 0 or negative errno code
111970479a40SFrank  */
ytphy_utp_read_lpa(struct phy_device * phydev)112070479a40SFrank static int ytphy_utp_read_lpa(struct phy_device *phydev)
112170479a40SFrank {
112270479a40SFrank 	int lpa, lpagb;
112370479a40SFrank 
112470479a40SFrank 	if (phydev->autoneg == AUTONEG_ENABLE) {
112570479a40SFrank 		if (!phydev->autoneg_complete) {
112670479a40SFrank 			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
112770479a40SFrank 							0);
112870479a40SFrank 			mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
112970479a40SFrank 			return 0;
113070479a40SFrank 		}
113170479a40SFrank 
113270479a40SFrank 		if (phydev->is_gigabit_capable) {
113370479a40SFrank 			lpagb = __phy_read(phydev, MII_STAT1000);
113470479a40SFrank 			if (lpagb < 0)
113570479a40SFrank 				return lpagb;
113670479a40SFrank 
113770479a40SFrank 			if (lpagb & LPA_1000MSFAIL) {
113870479a40SFrank 				int adv = __phy_read(phydev, MII_CTRL1000);
113970479a40SFrank 
114070479a40SFrank 				if (adv < 0)
114170479a40SFrank 					return adv;
114270479a40SFrank 
114370479a40SFrank 				if (adv & CTL1000_ENABLE_MASTER)
114470479a40SFrank 					phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
114570479a40SFrank 				else
114670479a40SFrank 					phydev_err(phydev, "Master/Slave resolution failed\n");
114770479a40SFrank 				return -ENOLINK;
114870479a40SFrank 			}
114970479a40SFrank 
115070479a40SFrank 			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
115170479a40SFrank 							lpagb);
115270479a40SFrank 		}
115370479a40SFrank 
115470479a40SFrank 		lpa = __phy_read(phydev, MII_LPA);
115570479a40SFrank 		if (lpa < 0)
115670479a40SFrank 			return lpa;
115770479a40SFrank 
115870479a40SFrank 		mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
115970479a40SFrank 	} else {
116070479a40SFrank 		linkmode_zero(phydev->lp_advertising);
116170479a40SFrank 	}
116270479a40SFrank 
116370479a40SFrank 	return 0;
116470479a40SFrank }
116570479a40SFrank 
116670479a40SFrank /**
116770479a40SFrank  * yt8521_adjust_status() - update speed and duplex to phydev. when in fiber
116870479a40SFrank  * mode, adjust speed and duplex.
116970479a40SFrank  * @phydev: a pointer to a &struct phy_device
117070479a40SFrank  * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG
117170479a40SFrank  * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode)
117270479a40SFrank  *
117370479a40SFrank  * NOTE:The caller must have taken the MDIO bus lock.
117470479a40SFrank  *
117570479a40SFrank  * returns 0
117670479a40SFrank  */
yt8521_adjust_status(struct phy_device * phydev,int status,bool is_utp)117770479a40SFrank static int yt8521_adjust_status(struct phy_device *phydev, int status,
117870479a40SFrank 				bool is_utp)
117970479a40SFrank {
118070479a40SFrank 	int speed_mode, duplex;
118170479a40SFrank 	int speed;
118270479a40SFrank 	int err;
118370479a40SFrank 	int lpa;
118470479a40SFrank 
118570479a40SFrank 	if (is_utp)
118670479a40SFrank 		duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET;
118770479a40SFrank 	else
118870479a40SFrank 		duplex = DUPLEX_FULL;	/* for fiber, it always DUPLEX_FULL */
118970479a40SFrank 
119070479a40SFrank 	speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >>
119170479a40SFrank 		     YTPHY_SSR_SPEED_MODE_OFFSET;
119270479a40SFrank 
119370479a40SFrank 	switch (speed_mode) {
119470479a40SFrank 	case YTPHY_SSR_SPEED_10M:
119570479a40SFrank 		if (is_utp)
119670479a40SFrank 			speed = SPEED_10;
119770479a40SFrank 		else
119870479a40SFrank 			/* for fiber, it will never run here, default to
119970479a40SFrank 			 * SPEED_UNKNOWN
120070479a40SFrank 			 */
120170479a40SFrank 			speed = SPEED_UNKNOWN;
120270479a40SFrank 		break;
120370479a40SFrank 	case YTPHY_SSR_SPEED_100M:
120470479a40SFrank 		speed = SPEED_100;
120570479a40SFrank 		break;
120670479a40SFrank 	case YTPHY_SSR_SPEED_1000M:
120770479a40SFrank 		speed = SPEED_1000;
120870479a40SFrank 		break;
120970479a40SFrank 	default:
121070479a40SFrank 		speed = SPEED_UNKNOWN;
121170479a40SFrank 		break;
121270479a40SFrank 	}
121370479a40SFrank 
121470479a40SFrank 	phydev->speed = speed;
121570479a40SFrank 	phydev->duplex = duplex;
121670479a40SFrank 
121770479a40SFrank 	if (is_utp) {
121870479a40SFrank 		err = ytphy_utp_read_lpa(phydev);
121970479a40SFrank 		if (err < 0)
122070479a40SFrank 			return err;
122170479a40SFrank 
122270479a40SFrank 		phy_resolve_aneg_pause(phydev);
122370479a40SFrank 	} else {
122470479a40SFrank 		lpa = __phy_read(phydev, MII_LPA);
122570479a40SFrank 		if (lpa < 0)
122670479a40SFrank 			return lpa;
122770479a40SFrank 
122870479a40SFrank 		/* only support 1000baseX Full */
122970479a40SFrank 		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
123070479a40SFrank 				 phydev->lp_advertising, lpa & LPA_1000XFULL);
123170479a40SFrank 
123270479a40SFrank 		if (!(lpa & YTPHY_FLPA_PAUSE)) {
123370479a40SFrank 			phydev->pause = 0;
123470479a40SFrank 			phydev->asym_pause = 0;
123570479a40SFrank 		} else if ((lpa & YTPHY_FLPA_ASYM_PAUSE)) {
123670479a40SFrank 			phydev->pause = 1;
123770479a40SFrank 			phydev->asym_pause = 1;
123870479a40SFrank 		} else {
123970479a40SFrank 			phydev->pause = 1;
124070479a40SFrank 			phydev->asym_pause = 0;
124170479a40SFrank 		}
124270479a40SFrank 	}
124370479a40SFrank 
124470479a40SFrank 	return 0;
124570479a40SFrank }
124670479a40SFrank 
124770479a40SFrank /**
124870479a40SFrank  * yt8521_read_status_paged() -  determines the speed and duplex of one page
124970479a40SFrank  * @phydev: a pointer to a &struct phy_device
125070479a40SFrank  * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
125170479a40SFrank  * operate.
125270479a40SFrank  *
125370479a40SFrank  * returns 1 (utp or fiber link),0 (no link) or negative errno code
125470479a40SFrank  */
yt8521_read_status_paged(struct phy_device * phydev,int page)125570479a40SFrank static int yt8521_read_status_paged(struct phy_device *phydev, int page)
125670479a40SFrank {
125770479a40SFrank 	int fiber_latch_val;
125870479a40SFrank 	int fiber_curr_val;
125970479a40SFrank 	int old_page;
126070479a40SFrank 	int ret = 0;
126170479a40SFrank 	int status;
126270479a40SFrank 	int link;
126370479a40SFrank 
126470479a40SFrank 	linkmode_zero(phydev->lp_advertising);
126570479a40SFrank 	phydev->duplex = DUPLEX_UNKNOWN;
126670479a40SFrank 	phydev->speed = SPEED_UNKNOWN;
126770479a40SFrank 	phydev->asym_pause = 0;
126870479a40SFrank 	phydev->pause = 0;
126970479a40SFrank 
127070479a40SFrank 	/* YT8521 has two reg space (utp/fiber) for linkup with utp/fiber
127170479a40SFrank 	 * respectively. but for utp/fiber combo mode, reg space should be
127270479a40SFrank 	 * arbitrated based on media priority. by default, utp takes
127370479a40SFrank 	 * priority. reg space should be properly set before read
127470479a40SFrank 	 * YTPHY_SPECIFIC_STATUS_REG.
127570479a40SFrank 	 */
127670479a40SFrank 
127770479a40SFrank 	page &= YT8521_RSSR_SPACE_MASK;
127870479a40SFrank 	old_page = phy_select_page(phydev, page);
127970479a40SFrank 	if (old_page < 0)
128070479a40SFrank 		goto err_restore_page;
128170479a40SFrank 
128270479a40SFrank 	/* Read YTPHY_SPECIFIC_STATUS_REG, which indicates the speed and duplex
128370479a40SFrank 	 * of the PHY is actually using.
128470479a40SFrank 	 */
128570479a40SFrank 	ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
128670479a40SFrank 	if (ret < 0)
128770479a40SFrank 		goto err_restore_page;
128870479a40SFrank 
128970479a40SFrank 	status = ret;
129070479a40SFrank 	link = !!(status & YTPHY_SSR_LINK);
129170479a40SFrank 
129270479a40SFrank 	/* When PHY is in fiber mode, speed transferred from 1000Mbps to
129370479a40SFrank 	 * 100Mbps,there is not link down from YTPHY_SPECIFIC_STATUS_REG, so
129470479a40SFrank 	 * we need check MII_BMSR to identify such case.
129570479a40SFrank 	 */
129670479a40SFrank 	if (page == YT8521_RSSR_FIBER_SPACE) {
129770479a40SFrank 		ret = __phy_read(phydev, MII_BMSR);
129870479a40SFrank 		if (ret < 0)
129970479a40SFrank 			goto err_restore_page;
130070479a40SFrank 
130170479a40SFrank 		fiber_latch_val = ret;
130270479a40SFrank 		ret = __phy_read(phydev, MII_BMSR);
130370479a40SFrank 		if (ret < 0)
130470479a40SFrank 			goto err_restore_page;
130570479a40SFrank 
130670479a40SFrank 		fiber_curr_val = ret;
130770479a40SFrank 		if (link && fiber_latch_val != fiber_curr_val) {
130870479a40SFrank 			link = 0;
130970479a40SFrank 			phydev_info(phydev,
131070479a40SFrank 				    "%s, fiber link down detect, latch = %04x, curr = %04x\n",
131170479a40SFrank 				    __func__, fiber_latch_val, fiber_curr_val);
131270479a40SFrank 		}
131370479a40SFrank 	} else {
131470479a40SFrank 		/* Read autonegotiation status */
131570479a40SFrank 		ret = __phy_read(phydev, MII_BMSR);
131670479a40SFrank 		if (ret < 0)
131770479a40SFrank 			goto err_restore_page;
131870479a40SFrank 
131970479a40SFrank 		phydev->autoneg_complete = ret & BMSR_ANEGCOMPLETE ? 1 : 0;
132070479a40SFrank 	}
132170479a40SFrank 
132270479a40SFrank 	if (link) {
132370479a40SFrank 		if (page == YT8521_RSSR_UTP_SPACE)
132470479a40SFrank 			yt8521_adjust_status(phydev, status, true);
132570479a40SFrank 		else
132670479a40SFrank 			yt8521_adjust_status(phydev, status, false);
132770479a40SFrank 	}
132870479a40SFrank 	return phy_restore_page(phydev, old_page, link);
132970479a40SFrank 
133070479a40SFrank err_restore_page:
133170479a40SFrank 	return phy_restore_page(phydev, old_page, ret);
133270479a40SFrank }
133370479a40SFrank 
133470479a40SFrank /**
133570479a40SFrank  * yt8521_read_status() -  determines the negotiated speed and duplex
133670479a40SFrank  * @phydev: a pointer to a &struct phy_device
133770479a40SFrank  *
133870479a40SFrank  * returns 0 or negative errno code
133970479a40SFrank  */
yt8521_read_status(struct phy_device * phydev)134070479a40SFrank static int yt8521_read_status(struct phy_device *phydev)
134170479a40SFrank {
134270479a40SFrank 	struct yt8521_priv *priv = phydev->priv;
134370479a40SFrank 	int link_fiber = 0;
134470479a40SFrank 	int link_utp;
134570479a40SFrank 	int link;
134670479a40SFrank 	int ret;
134770479a40SFrank 
134870479a40SFrank 	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
134970479a40SFrank 		link = yt8521_read_status_paged(phydev, priv->reg_page);
135070479a40SFrank 		if (link < 0)
135170479a40SFrank 			return link;
135270479a40SFrank 	} else {
135370479a40SFrank 		/* when page is YT8521_RSSR_TO_BE_ARBITRATED, arbitration is
135470479a40SFrank 		 * needed. by default, utp is higher priority.
135570479a40SFrank 		 */
135670479a40SFrank 
135770479a40SFrank 		link_utp = yt8521_read_status_paged(phydev,
135870479a40SFrank 						    YT8521_RSSR_UTP_SPACE);
135970479a40SFrank 		if (link_utp < 0)
136070479a40SFrank 			return link_utp;
136170479a40SFrank 
136270479a40SFrank 		if (!link_utp) {
136370479a40SFrank 			link_fiber = yt8521_read_status_paged(phydev,
136470479a40SFrank 							      YT8521_RSSR_FIBER_SPACE);
136570479a40SFrank 			if (link_fiber < 0)
136670479a40SFrank 				return link_fiber;
136770479a40SFrank 		}
136870479a40SFrank 
136970479a40SFrank 		link = link_utp || link_fiber;
137070479a40SFrank 	}
137170479a40SFrank 
137270479a40SFrank 	if (link) {
137370479a40SFrank 		if (phydev->link == 0) {
137470479a40SFrank 			/* arbitrate reg space based on linkup media type. */
137570479a40SFrank 			if (priv->polling_mode == YT8521_MODE_POLL &&
137670479a40SFrank 			    priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
137770479a40SFrank 				if (link_fiber)
137870479a40SFrank 					priv->reg_page =
137970479a40SFrank 						YT8521_RSSR_FIBER_SPACE;
138070479a40SFrank 				else
138170479a40SFrank 					priv->reg_page = YT8521_RSSR_UTP_SPACE;
138270479a40SFrank 
138370479a40SFrank 				ret = ytphy_write_ext_with_lock(phydev,
138470479a40SFrank 								YT8521_REG_SPACE_SELECT_REG,
138570479a40SFrank 								priv->reg_page);
138670479a40SFrank 				if (ret < 0)
138770479a40SFrank 					return ret;
138870479a40SFrank 
138970479a40SFrank 				phydev->port = link_fiber ? PORT_FIBRE : PORT_TP;
139070479a40SFrank 
139170479a40SFrank 				phydev_info(phydev, "%s, link up, media: %s\n",
139270479a40SFrank 					    __func__,
139370479a40SFrank 					    (phydev->port == PORT_TP) ?
139470479a40SFrank 					    "UTP" : "Fiber");
139570479a40SFrank 			}
139670479a40SFrank 		}
139770479a40SFrank 		phydev->link = 1;
139870479a40SFrank 	} else {
139970479a40SFrank 		if (phydev->link == 1) {
140070479a40SFrank 			phydev_info(phydev, "%s, link down, media: %s\n",
140170479a40SFrank 				    __func__, (phydev->port == PORT_TP) ?
140270479a40SFrank 				    "UTP" : "Fiber");
140370479a40SFrank 
140470479a40SFrank 			/* When in YT8521_MODE_POLL mode, need prepare for next
140570479a40SFrank 			 * arbitration.
140670479a40SFrank 			 */
140770479a40SFrank 			if (priv->polling_mode == YT8521_MODE_POLL) {
140870479a40SFrank 				priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
140970479a40SFrank 				phydev->port = PORT_NONE;
141070479a40SFrank 			}
141170479a40SFrank 		}
141270479a40SFrank 
141370479a40SFrank 		phydev->link = 0;
141470479a40SFrank 	}
141570479a40SFrank 
141670479a40SFrank 	return 0;
141770479a40SFrank }
141870479a40SFrank 
141970479a40SFrank /**
142070479a40SFrank  * yt8521_modify_bmcr_paged - bits modify a PHY's BMCR register of one page
142170479a40SFrank  * @phydev: the phy_device struct
142270479a40SFrank  * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to operate
142370479a40SFrank  * @mask: bit mask of bits to clear
142470479a40SFrank  * @set: bit mask of bits to set
142570479a40SFrank  *
142670479a40SFrank  * NOTE: Convenience function which allows a PHY's BMCR register to be
142770479a40SFrank  * modified as new register value = (old register value & ~mask) | set.
142870479a40SFrank  * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
142970479a40SFrank  * has MII_BMCR. poll mode combines utp and faber,so need do both.
143070479a40SFrank  * If it is reset, it will wait for completion.
143170479a40SFrank  *
143270479a40SFrank  * returns 0 or negative errno code
143370479a40SFrank  */
yt8521_modify_bmcr_paged(struct phy_device * phydev,int page,u16 mask,u16 set)143470479a40SFrank static int yt8521_modify_bmcr_paged(struct phy_device *phydev, int page,
143570479a40SFrank 				    u16 mask, u16 set)
143670479a40SFrank {
143770479a40SFrank 	int max_cnt = 500; /* the max wait time of reset ~ 500 ms */
143870479a40SFrank 	int old_page;
143970479a40SFrank 	int ret = 0;
144070479a40SFrank 
144170479a40SFrank 	old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
144270479a40SFrank 	if (old_page < 0)
144370479a40SFrank 		goto err_restore_page;
144470479a40SFrank 
144570479a40SFrank 	ret = __phy_modify(phydev, MII_BMCR, mask, set);
144670479a40SFrank 	if (ret < 0)
144770479a40SFrank 		goto err_restore_page;
144870479a40SFrank 
144970479a40SFrank 	/* If it is reset, need to wait for the reset to complete */
145070479a40SFrank 	if (set == BMCR_RESET) {
145170479a40SFrank 		while (max_cnt--) {
145270479a40SFrank 			usleep_range(1000, 1100);
145370479a40SFrank 			ret = __phy_read(phydev, MII_BMCR);
145470479a40SFrank 			if (ret < 0)
145570479a40SFrank 				goto err_restore_page;
145670479a40SFrank 
145770479a40SFrank 			if (!(ret & BMCR_RESET))
145870479a40SFrank 				return phy_restore_page(phydev, old_page, 0);
145970479a40SFrank 		}
146070479a40SFrank 	}
146170479a40SFrank 
146270479a40SFrank err_restore_page:
146370479a40SFrank 	return phy_restore_page(phydev, old_page, ret);
146470479a40SFrank }
146570479a40SFrank 
146670479a40SFrank /**
146770479a40SFrank  * yt8521_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register
146870479a40SFrank  * @phydev: the phy_device struct
146970479a40SFrank  * @mask: bit mask of bits to clear
147070479a40SFrank  * @set: bit mask of bits to set
147170479a40SFrank  *
147270479a40SFrank  * NOTE: Convenience function which allows a PHY's BMCR register to be
147370479a40SFrank  * modified as new register value = (old register value & ~mask) | set.
147470479a40SFrank  * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
147570479a40SFrank  * has MII_BMCR. poll mode combines utp and faber,so need do both.
147670479a40SFrank  *
147770479a40SFrank  * returns 0 or negative errno code
147870479a40SFrank  */
yt8521_modify_utp_fiber_bmcr(struct phy_device * phydev,u16 mask,u16 set)147970479a40SFrank static int yt8521_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask,
148070479a40SFrank 					u16 set)
148170479a40SFrank {
148270479a40SFrank 	struct yt8521_priv *priv = phydev->priv;
148370479a40SFrank 	int ret;
148470479a40SFrank 
148570479a40SFrank 	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
148670479a40SFrank 		ret = yt8521_modify_bmcr_paged(phydev, priv->reg_page, mask,
148770479a40SFrank 					       set);
148870479a40SFrank 		if (ret < 0)
148970479a40SFrank 			return ret;
149070479a40SFrank 	} else {
149170479a40SFrank 		ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE,
149270479a40SFrank 					       mask, set);
149370479a40SFrank 		if (ret < 0)
149470479a40SFrank 			return ret;
149570479a40SFrank 
149670479a40SFrank 		ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE,
149770479a40SFrank 					       mask, set);
149870479a40SFrank 		if (ret < 0)
149970479a40SFrank 			return ret;
150070479a40SFrank 	}
150170479a40SFrank 	return 0;
150270479a40SFrank }
150370479a40SFrank 
150470479a40SFrank /**
150570479a40SFrank  * yt8521_soft_reset() - called to issue a PHY software reset
150670479a40SFrank  * @phydev: a pointer to a &struct phy_device
150770479a40SFrank  *
150870479a40SFrank  * returns 0 or negative errno code
150970479a40SFrank  */
yt8521_soft_reset(struct phy_device * phydev)151070479a40SFrank static int yt8521_soft_reset(struct phy_device *phydev)
151170479a40SFrank {
151270479a40SFrank 	return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_RESET);
151370479a40SFrank }
151470479a40SFrank 
151570479a40SFrank /**
151670479a40SFrank  * yt8521_suspend() - suspend the hardware
151770479a40SFrank  * @phydev: a pointer to a &struct phy_device
151870479a40SFrank  *
151970479a40SFrank  * returns 0 or negative errno code
152070479a40SFrank  */
yt8521_suspend(struct phy_device * phydev)152170479a40SFrank static int yt8521_suspend(struct phy_device *phydev)
152270479a40SFrank {
152370479a40SFrank 	int wol_config;
152470479a40SFrank 
152570479a40SFrank 	/* YTPHY_WOL_CONFIG_REG is common ext reg */
152670479a40SFrank 	wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
152770479a40SFrank 	if (wol_config < 0)
152870479a40SFrank 		return wol_config;
152970479a40SFrank 
153070479a40SFrank 	/* if wol enable, do nothing */
153170479a40SFrank 	if (wol_config & YTPHY_WCR_ENABLE)
153270479a40SFrank 		return 0;
153370479a40SFrank 
153470479a40SFrank 	return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN);
153570479a40SFrank }
153670479a40SFrank 
153770479a40SFrank /**
153870479a40SFrank  * yt8521_resume() - resume the hardware
153970479a40SFrank  * @phydev: a pointer to a &struct phy_device
154070479a40SFrank  *
154170479a40SFrank  * returns 0 or negative errno code
154270479a40SFrank  */
yt8521_resume(struct phy_device * phydev)154370479a40SFrank static int yt8521_resume(struct phy_device *phydev)
154470479a40SFrank {
154570479a40SFrank 	int ret;
154670479a40SFrank 	int wol_config;
154770479a40SFrank 
154870479a40SFrank 	/* disable auto sleep */
154970479a40SFrank 	ret = ytphy_modify_ext_with_lock(phydev,
155070479a40SFrank 					 YT8521_EXTREG_SLEEP_CONTROL1_REG,
155170479a40SFrank 					 YT8521_ESC1R_SLEEP_SW, 0);
155270479a40SFrank 	if (ret < 0)
155370479a40SFrank 		return ret;
155470479a40SFrank 
155570479a40SFrank 	wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
155670479a40SFrank 	if (wol_config < 0)
155770479a40SFrank 		return wol_config;
155870479a40SFrank 
155970479a40SFrank 	/* if wol enable, do nothing */
156070479a40SFrank 	if (wol_config & YTPHY_WCR_ENABLE)
156170479a40SFrank 		return 0;
156270479a40SFrank 
156370479a40SFrank 	return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
156470479a40SFrank }
156570479a40SFrank 
156670479a40SFrank /**
156770479a40SFrank  * yt8521_config_init() - called to initialize the PHY
156870479a40SFrank  * @phydev: a pointer to a &struct phy_device
156970479a40SFrank  *
157070479a40SFrank  * returns 0 or negative errno code
157170479a40SFrank  */
yt8521_config_init(struct phy_device * phydev)157270479a40SFrank static int yt8521_config_init(struct phy_device *phydev)
157370479a40SFrank {
1574a6e68f0fSFrank Sae 	struct device_node *node = phydev->mdio.dev.of_node;
157570479a40SFrank 	int old_page;
157670479a40SFrank 	int ret = 0;
157770479a40SFrank 
157870479a40SFrank 	old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
157970479a40SFrank 	if (old_page < 0)
158070479a40SFrank 		goto err_restore_page;
158170479a40SFrank 
158270479a40SFrank 	/* set rgmii delay mode */
158370479a40SFrank 	if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
1584a6e68f0fSFrank Sae 		ret = ytphy_rgmii_clk_delay_config(phydev);
158570479a40SFrank 		if (ret < 0)
158670479a40SFrank 			goto err_restore_page;
158770479a40SFrank 	}
158870479a40SFrank 
1589a6e68f0fSFrank Sae 	if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
159070479a40SFrank 		/* disable auto sleep */
159170479a40SFrank 		ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
159270479a40SFrank 				       YT8521_ESC1R_SLEEP_SW, 0);
159370479a40SFrank 		if (ret < 0)
159470479a40SFrank 			goto err_restore_page;
1595a6e68f0fSFrank Sae 	}
159670479a40SFrank 
1597a6e68f0fSFrank Sae 	if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
159870479a40SFrank 		/* enable RXC clock when no wire plug */
159970479a40SFrank 		ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
160070479a40SFrank 				       YT8521_CGR_RX_CLK_EN, 0);
160170479a40SFrank 		if (ret < 0)
160270479a40SFrank 			goto err_restore_page;
1603a6e68f0fSFrank Sae 	}
160470479a40SFrank err_restore_page:
160570479a40SFrank 	return phy_restore_page(phydev, old_page, ret);
160670479a40SFrank }
160770479a40SFrank 
yt8531_config_init(struct phy_device * phydev)16084ac94f72SFrank Sae static int yt8531_config_init(struct phy_device *phydev)
16094ac94f72SFrank Sae {
16104ac94f72SFrank Sae 	struct device_node *node = phydev->mdio.dev.of_node;
16114ac94f72SFrank Sae 	int ret;
16124ac94f72SFrank Sae 
16134ac94f72SFrank Sae 	ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
16144ac94f72SFrank Sae 	if (ret < 0)
16154ac94f72SFrank Sae 		return ret;
16164ac94f72SFrank Sae 
16174ac94f72SFrank Sae 	if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
16184ac94f72SFrank Sae 		/* disable auto sleep */
16194ac94f72SFrank Sae 		ret = ytphy_modify_ext_with_lock(phydev,
16204ac94f72SFrank Sae 						 YT8521_EXTREG_SLEEP_CONTROL1_REG,
16214ac94f72SFrank Sae 						 YT8521_ESC1R_SLEEP_SW, 0);
16224ac94f72SFrank Sae 		if (ret < 0)
16234ac94f72SFrank Sae 			return ret;
16244ac94f72SFrank Sae 	}
16254ac94f72SFrank Sae 
16264ac94f72SFrank Sae 	if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
16274ac94f72SFrank Sae 		/* enable RXC clock when no wire plug */
16284ac94f72SFrank Sae 		ret = ytphy_modify_ext_with_lock(phydev,
16294ac94f72SFrank Sae 						 YT8521_CLOCK_GATING_REG,
16304ac94f72SFrank Sae 						 YT8521_CGR_RX_CLK_EN, 0);
16314ac94f72SFrank Sae 		if (ret < 0)
16324ac94f72SFrank Sae 			return ret;
16334ac94f72SFrank Sae 	}
16344ac94f72SFrank Sae 
1635*7a561e93SSamin Guo 	ret = yt8531_set_ds(phydev);
1636*7a561e93SSamin Guo 	if (ret < 0)
1637*7a561e93SSamin Guo 		return ret;
1638*7a561e93SSamin Guo 
16394ac94f72SFrank Sae 	return 0;
16404ac94f72SFrank Sae }
16414ac94f72SFrank Sae 
16424ac94f72SFrank Sae /**
16434ac94f72SFrank Sae  * yt8531_link_change_notify() - Adjust the tx clock direction according to
16444ac94f72SFrank Sae  * the current speed and dts config.
16454ac94f72SFrank Sae  * @phydev: a pointer to a &struct phy_device
16464ac94f72SFrank Sae  *
16474ac94f72SFrank Sae  * NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please
16484ac94f72SFrank Sae  * keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not
16494ac94f72SFrank Sae  * JH7110.
16504ac94f72SFrank Sae  */
yt8531_link_change_notify(struct phy_device * phydev)16514ac94f72SFrank Sae static void yt8531_link_change_notify(struct phy_device *phydev)
16524ac94f72SFrank Sae {
16534ac94f72SFrank Sae 	struct device_node *node = phydev->mdio.dev.of_node;
16549753613fSDan Carpenter 	bool tx_clk_1000_inverted = false;
16559753613fSDan Carpenter 	bool tx_clk_100_inverted = false;
16569753613fSDan Carpenter 	bool tx_clk_10_inverted = false;
16574ac94f72SFrank Sae 	bool tx_clk_adj_enabled = false;
16584ac94f72SFrank Sae 	u16 val = 0;
16594ac94f72SFrank Sae 	int ret;
16604ac94f72SFrank Sae 
16614ac94f72SFrank Sae 	if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
16624ac94f72SFrank Sae 		tx_clk_adj_enabled = true;
16634ac94f72SFrank Sae 
16644ac94f72SFrank Sae 	if (!tx_clk_adj_enabled)
16654ac94f72SFrank Sae 		return;
16664ac94f72SFrank Sae 
16674ac94f72SFrank Sae 	if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted"))
16684ac94f72SFrank Sae 		tx_clk_10_inverted = true;
16694ac94f72SFrank Sae 	if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted"))
16704ac94f72SFrank Sae 		tx_clk_100_inverted = true;
16714ac94f72SFrank Sae 	if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted"))
16724ac94f72SFrank Sae 		tx_clk_1000_inverted = true;
16734ac94f72SFrank Sae 
16744ac94f72SFrank Sae 	if (phydev->speed < 0)
16754ac94f72SFrank Sae 		return;
16764ac94f72SFrank Sae 
16774ac94f72SFrank Sae 	switch (phydev->speed) {
16784ac94f72SFrank Sae 	case SPEED_1000:
16794ac94f72SFrank Sae 		if (tx_clk_1000_inverted)
16804ac94f72SFrank Sae 			val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
16814ac94f72SFrank Sae 		break;
16824ac94f72SFrank Sae 	case SPEED_100:
16834ac94f72SFrank Sae 		if (tx_clk_100_inverted)
16844ac94f72SFrank Sae 			val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
16854ac94f72SFrank Sae 		break;
16864ac94f72SFrank Sae 	case SPEED_10:
16874ac94f72SFrank Sae 		if (tx_clk_10_inverted)
16884ac94f72SFrank Sae 			val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
16894ac94f72SFrank Sae 		break;
16904ac94f72SFrank Sae 	default:
16914ac94f72SFrank Sae 		return;
16924ac94f72SFrank Sae 	}
16934ac94f72SFrank Sae 
16944ac94f72SFrank Sae 	ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
16954ac94f72SFrank Sae 					 YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
16964ac94f72SFrank Sae 	if (ret < 0)
16974ac94f72SFrank Sae 		phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
16984ac94f72SFrank Sae }
16994ac94f72SFrank Sae 
170070479a40SFrank /**
170170479a40SFrank  * yt8521_prepare_fiber_features() -  A small helper function that setup
170270479a40SFrank  * fiber's features.
170370479a40SFrank  * @phydev: a pointer to a &struct phy_device
170470479a40SFrank  * @dst: a pointer to store fiber's features
170570479a40SFrank  */
yt8521_prepare_fiber_features(struct phy_device * phydev,unsigned long * dst)170670479a40SFrank static void yt8521_prepare_fiber_features(struct phy_device *phydev,
170770479a40SFrank 					  unsigned long *dst)
170870479a40SFrank {
170970479a40SFrank 	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, dst);
171070479a40SFrank 	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, dst);
171170479a40SFrank 	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, dst);
171270479a40SFrank 	linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, dst);
171370479a40SFrank }
171470479a40SFrank 
171570479a40SFrank /**
171670479a40SFrank  * yt8521_fiber_setup_forced - configures/forces speed from @phydev
171770479a40SFrank  * @phydev: target phy_device struct
171870479a40SFrank  *
171970479a40SFrank  * NOTE:The caller must have taken the MDIO bus lock.
172070479a40SFrank  *
172170479a40SFrank  * returns 0 or negative errno code
172270479a40SFrank  */
yt8521_fiber_setup_forced(struct phy_device * phydev)172370479a40SFrank static int yt8521_fiber_setup_forced(struct phy_device *phydev)
172470479a40SFrank {
172570479a40SFrank 	u16 val;
172670479a40SFrank 	int ret;
172770479a40SFrank 
172870479a40SFrank 	if (phydev->speed == SPEED_1000)
172970479a40SFrank 		val = YTPHY_MCR_FIBER_1000BX;
173070479a40SFrank 	else if (phydev->speed == SPEED_100)
173170479a40SFrank 		val = YTPHY_MCR_FIBER_100FX;
173270479a40SFrank 	else
173370479a40SFrank 		return -EINVAL;
173470479a40SFrank 
173570479a40SFrank 	ret =  __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
173670479a40SFrank 	if (ret < 0)
173770479a40SFrank 		return ret;
173870479a40SFrank 
173970479a40SFrank 	/* disable Fiber auto sensing */
174070479a40SFrank 	ret =  ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
174170479a40SFrank 				YT8521_LTCR_EN_AUTOSEN, 0);
174270479a40SFrank 	if (ret < 0)
174370479a40SFrank 		return ret;
174470479a40SFrank 
174570479a40SFrank 	ret =  ytphy_modify_ext(phydev, YTPHY_MISC_CONFIG_REG,
174670479a40SFrank 				YTPHY_MCR_FIBER_SPEED_MASK, val);
174770479a40SFrank 	if (ret < 0)
174870479a40SFrank 		return ret;
174970479a40SFrank 
175070479a40SFrank 	return ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
175170479a40SFrank 				YT8521_CCR_SW_RST, 0);
175270479a40SFrank }
175370479a40SFrank 
175470479a40SFrank /**
175570479a40SFrank  * ytphy_check_and_restart_aneg - Enable and restart auto-negotiation
175670479a40SFrank  * @phydev: target phy_device struct
175770479a40SFrank  * @restart: whether aneg restart is requested
175870479a40SFrank  *
175970479a40SFrank  * NOTE:The caller must have taken the MDIO bus lock.
176070479a40SFrank  *
176170479a40SFrank  * returns 0 or negative errno code
176270479a40SFrank  */
ytphy_check_and_restart_aneg(struct phy_device * phydev,bool restart)176370479a40SFrank static int ytphy_check_and_restart_aneg(struct phy_device *phydev, bool restart)
176470479a40SFrank {
176570479a40SFrank 	int ret;
176670479a40SFrank 
176770479a40SFrank 	if (!restart) {
176870479a40SFrank 		/* Advertisement hasn't changed, but maybe aneg was never on to
176970479a40SFrank 		 * begin with?  Or maybe phy was isolated?
177070479a40SFrank 		 */
177170479a40SFrank 		ret = __phy_read(phydev, MII_BMCR);
177270479a40SFrank 		if (ret < 0)
177370479a40SFrank 			return ret;
177470479a40SFrank 
177570479a40SFrank 		if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE))
177670479a40SFrank 			restart = true;
177770479a40SFrank 	}
177870479a40SFrank 	/* Enable and Restart Autonegotiation
177970479a40SFrank 	 * Don't isolate the PHY if we're negotiating
178070479a40SFrank 	 */
178170479a40SFrank 	if (restart)
178270479a40SFrank 		return __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
178370479a40SFrank 				    BMCR_ANENABLE | BMCR_ANRESTART);
178470479a40SFrank 
178570479a40SFrank 	return 0;
178670479a40SFrank }
178770479a40SFrank 
178870479a40SFrank /**
178970479a40SFrank  * yt8521_fiber_config_aneg - restart auto-negotiation or write
179070479a40SFrank  * YTPHY_MISC_CONFIG_REG.
179170479a40SFrank  * @phydev: target phy_device struct
179270479a40SFrank  *
179370479a40SFrank  * NOTE:The caller must have taken the MDIO bus lock.
179470479a40SFrank  *
179570479a40SFrank  * returns 0 or negative errno code
179670479a40SFrank  */
yt8521_fiber_config_aneg(struct phy_device * phydev)179770479a40SFrank static int yt8521_fiber_config_aneg(struct phy_device *phydev)
179870479a40SFrank {
179970479a40SFrank 	int err, changed = 0;
180070479a40SFrank 	int bmcr;
180170479a40SFrank 	u16 adv;
180270479a40SFrank 
180370479a40SFrank 	if (phydev->autoneg != AUTONEG_ENABLE)
180470479a40SFrank 		return yt8521_fiber_setup_forced(phydev);
180570479a40SFrank 
180670479a40SFrank 	/* enable Fiber auto sensing */
180770479a40SFrank 	err =  ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
180870479a40SFrank 				0, YT8521_LTCR_EN_AUTOSEN);
180970479a40SFrank 	if (err < 0)
181070479a40SFrank 		return err;
181170479a40SFrank 
181270479a40SFrank 	err =  ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
181370479a40SFrank 				YT8521_CCR_SW_RST, 0);
181470479a40SFrank 	if (err < 0)
181570479a40SFrank 		return err;
181670479a40SFrank 
181770479a40SFrank 	bmcr = __phy_read(phydev, MII_BMCR);
181870479a40SFrank 	if (bmcr < 0)
181970479a40SFrank 		return bmcr;
182070479a40SFrank 
182170479a40SFrank 	/* When it is coming from fiber forced mode, add bmcr power down
182270479a40SFrank 	 * and power up to let aneg work fine.
182370479a40SFrank 	 */
182470479a40SFrank 	if (!(bmcr & BMCR_ANENABLE)) {
182570479a40SFrank 		__phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
182670479a40SFrank 		usleep_range(1000, 1100);
182770479a40SFrank 		__phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
182870479a40SFrank 	}
182970479a40SFrank 
183070479a40SFrank 	adv = linkmode_adv_to_mii_adv_x(phydev->advertising,
183170479a40SFrank 					ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
183270479a40SFrank 
183370479a40SFrank 	/* Setup fiber advertisement */
183470479a40SFrank 	err = __phy_modify_changed(phydev, MII_ADVERTISE,
183570479a40SFrank 				   ADVERTISE_1000XHALF | ADVERTISE_1000XFULL |
183670479a40SFrank 				   ADVERTISE_1000XPAUSE |
183770479a40SFrank 				   ADVERTISE_1000XPSE_ASYM,
183870479a40SFrank 				   adv);
183970479a40SFrank 	if (err < 0)
184070479a40SFrank 		return err;
184170479a40SFrank 
184270479a40SFrank 	if (err > 0)
184370479a40SFrank 		changed = 1;
184470479a40SFrank 
184570479a40SFrank 	return ytphy_check_and_restart_aneg(phydev, changed);
184670479a40SFrank }
184770479a40SFrank 
184870479a40SFrank /**
184970479a40SFrank  * ytphy_setup_master_slave
185070479a40SFrank  * @phydev: target phy_device struct
185170479a40SFrank  *
185270479a40SFrank  * NOTE: The caller must have taken the MDIO bus lock.
185370479a40SFrank  *
185470479a40SFrank  * returns 0 or negative errno code
185570479a40SFrank  */
ytphy_setup_master_slave(struct phy_device * phydev)185670479a40SFrank static int ytphy_setup_master_slave(struct phy_device *phydev)
185770479a40SFrank {
185870479a40SFrank 	u16 ctl = 0;
185970479a40SFrank 
186070479a40SFrank 	if (!phydev->is_gigabit_capable)
186170479a40SFrank 		return 0;
186270479a40SFrank 
186370479a40SFrank 	switch (phydev->master_slave_set) {
186470479a40SFrank 	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
186570479a40SFrank 		ctl |= CTL1000_PREFER_MASTER;
186670479a40SFrank 		break;
186770479a40SFrank 	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
186870479a40SFrank 		break;
186970479a40SFrank 	case MASTER_SLAVE_CFG_MASTER_FORCE:
187070479a40SFrank 		ctl |= CTL1000_AS_MASTER;
187170479a40SFrank 		fallthrough;
187270479a40SFrank 	case MASTER_SLAVE_CFG_SLAVE_FORCE:
187370479a40SFrank 		ctl |= CTL1000_ENABLE_MASTER;
187470479a40SFrank 		break;
187570479a40SFrank 	case MASTER_SLAVE_CFG_UNKNOWN:
187670479a40SFrank 	case MASTER_SLAVE_CFG_UNSUPPORTED:
187770479a40SFrank 		return 0;
187870479a40SFrank 	default:
187970479a40SFrank 		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
188070479a40SFrank 		return -EOPNOTSUPP;
188170479a40SFrank 	}
188270479a40SFrank 
188370479a40SFrank 	return __phy_modify_changed(phydev, MII_CTRL1000,
188470479a40SFrank 				    (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
188570479a40SFrank 				    CTL1000_PREFER_MASTER), ctl);
188670479a40SFrank }
188770479a40SFrank 
188870479a40SFrank /**
188970479a40SFrank  * ytphy_utp_config_advert - sanitize and advertise auto-negotiation parameters
189070479a40SFrank  * @phydev: target phy_device struct
189170479a40SFrank  *
189270479a40SFrank  * NOTE: Writes MII_ADVERTISE with the appropriate values,
189370479a40SFrank  * after sanitizing the values to make sure we only advertise
189470479a40SFrank  * what is supported.  Returns < 0 on error, 0 if the PHY's advertisement
189570479a40SFrank  * hasn't changed, and > 0 if it has changed.
189670479a40SFrank  * The caller must have taken the MDIO bus lock.
189770479a40SFrank  *
189870479a40SFrank  * returns 0 or negative errno code
189970479a40SFrank  */
ytphy_utp_config_advert(struct phy_device * phydev)190070479a40SFrank static int ytphy_utp_config_advert(struct phy_device *phydev)
190170479a40SFrank {
190270479a40SFrank 	int err, bmsr, changed = 0;
190370479a40SFrank 	u32 adv;
190470479a40SFrank 
190570479a40SFrank 	/* Only allow advertising what this PHY supports */
190670479a40SFrank 	linkmode_and(phydev->advertising, phydev->advertising,
190770479a40SFrank 		     phydev->supported);
190870479a40SFrank 
190970479a40SFrank 	adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
191070479a40SFrank 
191170479a40SFrank 	/* Setup standard advertisement */
191270479a40SFrank 	err = __phy_modify_changed(phydev, MII_ADVERTISE,
191370479a40SFrank 				   ADVERTISE_ALL | ADVERTISE_100BASE4 |
191470479a40SFrank 				   ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
191570479a40SFrank 				   adv);
191670479a40SFrank 	if (err < 0)
191770479a40SFrank 		return err;
191870479a40SFrank 	if (err > 0)
191970479a40SFrank 		changed = 1;
192070479a40SFrank 
192170479a40SFrank 	bmsr = __phy_read(phydev, MII_BMSR);
192270479a40SFrank 	if (bmsr < 0)
192370479a40SFrank 		return bmsr;
192470479a40SFrank 
192570479a40SFrank 	/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
192670479a40SFrank 	 * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
192770479a40SFrank 	 * logical 1.
192870479a40SFrank 	 */
192970479a40SFrank 	if (!(bmsr & BMSR_ESTATEN))
193070479a40SFrank 		return changed;
193170479a40SFrank 
193270479a40SFrank 	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
193370479a40SFrank 
193470479a40SFrank 	err = __phy_modify_changed(phydev, MII_CTRL1000,
193570479a40SFrank 				   ADVERTISE_1000FULL | ADVERTISE_1000HALF,
193670479a40SFrank 				   adv);
193770479a40SFrank 	if (err < 0)
193870479a40SFrank 		return err;
193970479a40SFrank 	if (err > 0)
194070479a40SFrank 		changed = 1;
194170479a40SFrank 
194270479a40SFrank 	return changed;
194370479a40SFrank }
194470479a40SFrank 
194570479a40SFrank /**
194670479a40SFrank  * ytphy_utp_config_aneg - restart auto-negotiation or write BMCR
194770479a40SFrank  * @phydev: target phy_device struct
194870479a40SFrank  * @changed: whether autoneg is requested
194970479a40SFrank  *
195070479a40SFrank  * NOTE: If auto-negotiation is enabled, we configure the
195170479a40SFrank  * advertising, and then restart auto-negotiation.  If it is not
195270479a40SFrank  * enabled, then we write the BMCR.
195370479a40SFrank  * The caller must have taken the MDIO bus lock.
195470479a40SFrank  *
195570479a40SFrank  * returns 0 or negative errno code
195670479a40SFrank  */
ytphy_utp_config_aneg(struct phy_device * phydev,bool changed)195770479a40SFrank static int ytphy_utp_config_aneg(struct phy_device *phydev, bool changed)
195870479a40SFrank {
195970479a40SFrank 	int err;
196070479a40SFrank 	u16 ctl;
196170479a40SFrank 
196270479a40SFrank 	err = ytphy_setup_master_slave(phydev);
196370479a40SFrank 	if (err < 0)
196470479a40SFrank 		return err;
196570479a40SFrank 	else if (err)
196670479a40SFrank 		changed = true;
196770479a40SFrank 
196870479a40SFrank 	if (phydev->autoneg != AUTONEG_ENABLE) {
196970479a40SFrank 		/* configures/forces speed/duplex from @phydev */
197070479a40SFrank 
197170479a40SFrank 		ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
197270479a40SFrank 
197370479a40SFrank 		return __phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK |
197470479a40SFrank 				    BMCR_ISOLATE | BMCR_PDOWN), ctl);
197570479a40SFrank 	}
197670479a40SFrank 
197770479a40SFrank 	err = ytphy_utp_config_advert(phydev);
197870479a40SFrank 	if (err < 0) /* error */
197970479a40SFrank 		return err;
198070479a40SFrank 	else if (err)
198170479a40SFrank 		changed = true;
198270479a40SFrank 
198370479a40SFrank 	return ytphy_check_and_restart_aneg(phydev, changed);
198470479a40SFrank }
198570479a40SFrank 
198670479a40SFrank /**
198770479a40SFrank  * yt8521_config_aneg_paged() - switch reg space then call genphy_config_aneg
198870479a40SFrank  * of one page
198970479a40SFrank  * @phydev: a pointer to a &struct phy_device
199070479a40SFrank  * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
199170479a40SFrank  * operate.
199270479a40SFrank  *
199370479a40SFrank  * returns 0 or negative errno code
199470479a40SFrank  */
yt8521_config_aneg_paged(struct phy_device * phydev,int page)199570479a40SFrank static int yt8521_config_aneg_paged(struct phy_device *phydev, int page)
199670479a40SFrank {
199770479a40SFrank 	__ETHTOOL_DECLARE_LINK_MODE_MASK(fiber_supported);
199870479a40SFrank 	struct yt8521_priv *priv = phydev->priv;
199970479a40SFrank 	int old_page;
200070479a40SFrank 	int ret = 0;
200170479a40SFrank 
200270479a40SFrank 	page &= YT8521_RSSR_SPACE_MASK;
200370479a40SFrank 
200470479a40SFrank 	old_page = phy_select_page(phydev, page);
200570479a40SFrank 	if (old_page < 0)
200670479a40SFrank 		goto err_restore_page;
200770479a40SFrank 
200870479a40SFrank 	/* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
200970479a40SFrank 	 * phydev->advertising should be updated.
201070479a40SFrank 	 */
201170479a40SFrank 	if (priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
201270479a40SFrank 		linkmode_zero(fiber_supported);
201370479a40SFrank 		yt8521_prepare_fiber_features(phydev, fiber_supported);
201470479a40SFrank 
201570479a40SFrank 		/* prepare fiber_supported, then setup advertising. */
201670479a40SFrank 		if (page == YT8521_RSSR_FIBER_SPACE) {
201770479a40SFrank 			linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
201870479a40SFrank 					 fiber_supported);
201970479a40SFrank 			linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
202070479a40SFrank 					 fiber_supported);
202170479a40SFrank 			linkmode_and(phydev->advertising,
202270479a40SFrank 				     priv->combo_advertising, fiber_supported);
202370479a40SFrank 		} else {
202470479a40SFrank 			/* ETHTOOL_LINK_MODE_Autoneg_BIT is also used in utp */
202570479a40SFrank 			linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
202670479a40SFrank 					   fiber_supported);
202770479a40SFrank 			linkmode_andnot(phydev->advertising,
202870479a40SFrank 					priv->combo_advertising,
202970479a40SFrank 					fiber_supported);
203070479a40SFrank 		}
203170479a40SFrank 	}
203270479a40SFrank 
203370479a40SFrank 	if (page == YT8521_RSSR_FIBER_SPACE)
203470479a40SFrank 		ret = yt8521_fiber_config_aneg(phydev);
203570479a40SFrank 	else
203670479a40SFrank 		ret = ytphy_utp_config_aneg(phydev, false);
203770479a40SFrank 
203870479a40SFrank err_restore_page:
203970479a40SFrank 	return phy_restore_page(phydev, old_page, ret);
204070479a40SFrank }
204170479a40SFrank 
204270479a40SFrank /**
204370479a40SFrank  * yt8521_config_aneg() - change reg space then call yt8521_config_aneg_paged
204470479a40SFrank  * @phydev: a pointer to a &struct phy_device
204570479a40SFrank  *
204670479a40SFrank  * returns 0 or negative errno code
204770479a40SFrank  */
yt8521_config_aneg(struct phy_device * phydev)204870479a40SFrank static int yt8521_config_aneg(struct phy_device *phydev)
204970479a40SFrank {
205070479a40SFrank 	struct yt8521_priv *priv = phydev->priv;
205170479a40SFrank 	int ret;
205270479a40SFrank 
205370479a40SFrank 	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
205470479a40SFrank 		ret = yt8521_config_aneg_paged(phydev, priv->reg_page);
205570479a40SFrank 		if (ret < 0)
205670479a40SFrank 			return ret;
205770479a40SFrank 	} else {
205870479a40SFrank 		/* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
205970479a40SFrank 		 * phydev->advertising need to be saved at first run.
206070479a40SFrank 		 * Because it contains the advertising which supported by both
206170479a40SFrank 		 * mac and yt8521(utp and fiber).
206270479a40SFrank 		 */
206370479a40SFrank 		if (linkmode_empty(priv->combo_advertising)) {
206470479a40SFrank 			linkmode_copy(priv->combo_advertising,
206570479a40SFrank 				      phydev->advertising);
206670479a40SFrank 		}
206770479a40SFrank 
206870479a40SFrank 		ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_UTP_SPACE);
206970479a40SFrank 		if (ret < 0)
207070479a40SFrank 			return ret;
207170479a40SFrank 
207270479a40SFrank 		ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_FIBER_SPACE);
207370479a40SFrank 		if (ret < 0)
207470479a40SFrank 			return ret;
207570479a40SFrank 
207670479a40SFrank 		/* we don't known which will be link, so restore
207770479a40SFrank 		 * phydev->advertising as default value.
207870479a40SFrank 		 */
207970479a40SFrank 		linkmode_copy(phydev->advertising, priv->combo_advertising);
208070479a40SFrank 	}
208170479a40SFrank 	return 0;
208270479a40SFrank }
208370479a40SFrank 
208470479a40SFrank /**
208570479a40SFrank  * yt8521_aneg_done_paged() - determines the auto negotiation result of one
208670479a40SFrank  * page.
208770479a40SFrank  * @phydev: a pointer to a &struct phy_device
208870479a40SFrank  * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
208970479a40SFrank  * operate.
209070479a40SFrank  *
209170479a40SFrank  * returns 0(no link)or 1(fiber or utp link) or negative errno code
209270479a40SFrank  */
yt8521_aneg_done_paged(struct phy_device * phydev,int page)209370479a40SFrank static int yt8521_aneg_done_paged(struct phy_device *phydev, int page)
209470479a40SFrank {
209570479a40SFrank 	int old_page;
209670479a40SFrank 	int ret = 0;
209770479a40SFrank 	int link;
209870479a40SFrank 
209970479a40SFrank 	old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
210070479a40SFrank 	if (old_page < 0)
210170479a40SFrank 		goto err_restore_page;
210270479a40SFrank 
210370479a40SFrank 	ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
210470479a40SFrank 	if (ret < 0)
210570479a40SFrank 		goto err_restore_page;
210670479a40SFrank 
210770479a40SFrank 	link = !!(ret & YTPHY_SSR_LINK);
210870479a40SFrank 	ret = link;
210970479a40SFrank 
211070479a40SFrank err_restore_page:
211170479a40SFrank 	return phy_restore_page(phydev, old_page, ret);
211270479a40SFrank }
211370479a40SFrank 
211470479a40SFrank /**
211570479a40SFrank  * yt8521_aneg_done() - determines the auto negotiation result
211670479a40SFrank  * @phydev: a pointer to a &struct phy_device
211770479a40SFrank  *
211870479a40SFrank  * returns 0(no link)or 1(fiber or utp link) or negative errno code
211970479a40SFrank  */
yt8521_aneg_done(struct phy_device * phydev)212070479a40SFrank static int yt8521_aneg_done(struct phy_device *phydev)
212170479a40SFrank {
212270479a40SFrank 	struct yt8521_priv *priv = phydev->priv;
212370479a40SFrank 	int link_fiber = 0;
212470479a40SFrank 	int link_utp;
212570479a40SFrank 	int link;
212670479a40SFrank 
212770479a40SFrank 	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
212870479a40SFrank 		link = yt8521_aneg_done_paged(phydev, priv->reg_page);
212970479a40SFrank 	} else {
213070479a40SFrank 		link_utp = yt8521_aneg_done_paged(phydev,
213170479a40SFrank 						  YT8521_RSSR_UTP_SPACE);
213270479a40SFrank 		if (link_utp < 0)
213370479a40SFrank 			return link_utp;
213470479a40SFrank 
213570479a40SFrank 		if (!link_utp) {
213670479a40SFrank 			link_fiber = yt8521_aneg_done_paged(phydev,
213770479a40SFrank 							    YT8521_RSSR_FIBER_SPACE);
213870479a40SFrank 			if (link_fiber < 0)
213970479a40SFrank 				return link_fiber;
214070479a40SFrank 		}
214170479a40SFrank 		link = link_fiber || link_utp;
214270479a40SFrank 		phydev_info(phydev, "%s, link_fiber: %d, link_utp: %d\n",
214370479a40SFrank 			    __func__, link_fiber, link_utp);
214470479a40SFrank 	}
214570479a40SFrank 
214670479a40SFrank 	return link;
214770479a40SFrank }
214870479a40SFrank 
214970479a40SFrank /**
215070479a40SFrank  * ytphy_utp_read_abilities - read PHY abilities from Clause 22 registers
215170479a40SFrank  * @phydev: target phy_device struct
215270479a40SFrank  *
215370479a40SFrank  * NOTE: Reads the PHY's abilities and populates
215470479a40SFrank  * phydev->supported accordingly.
215570479a40SFrank  * The caller must have taken the MDIO bus lock.
215670479a40SFrank  *
215770479a40SFrank  * returns 0 or negative errno code
215870479a40SFrank  */
ytphy_utp_read_abilities(struct phy_device * phydev)215970479a40SFrank static int ytphy_utp_read_abilities(struct phy_device *phydev)
216070479a40SFrank {
216170479a40SFrank 	int val;
216270479a40SFrank 
216370479a40SFrank 	linkmode_set_bit_array(phy_basic_ports_array,
216470479a40SFrank 			       ARRAY_SIZE(phy_basic_ports_array),
216570479a40SFrank 			       phydev->supported);
216670479a40SFrank 
216770479a40SFrank 	val = __phy_read(phydev, MII_BMSR);
216870479a40SFrank 	if (val < 0)
216970479a40SFrank 		return val;
217070479a40SFrank 
217170479a40SFrank 	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported,
217270479a40SFrank 			 val & BMSR_ANEGCAPABLE);
217370479a40SFrank 
217470479a40SFrank 	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported,
217570479a40SFrank 			 val & BMSR_100FULL);
217670479a40SFrank 	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported,
217770479a40SFrank 			 val & BMSR_100HALF);
217870479a40SFrank 	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported,
217970479a40SFrank 			 val & BMSR_10FULL);
218070479a40SFrank 	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported,
218170479a40SFrank 			 val & BMSR_10HALF);
218270479a40SFrank 
218370479a40SFrank 	if (val & BMSR_ESTATEN) {
218470479a40SFrank 		val = __phy_read(phydev, MII_ESTATUS);
218570479a40SFrank 		if (val < 0)
218670479a40SFrank 			return val;
218770479a40SFrank 
218870479a40SFrank 		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
218970479a40SFrank 				 phydev->supported, val & ESTATUS_1000_TFULL);
219070479a40SFrank 		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
219170479a40SFrank 				 phydev->supported, val & ESTATUS_1000_THALF);
219270479a40SFrank 		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
219370479a40SFrank 				 phydev->supported, val & ESTATUS_1000_XFULL);
219470479a40SFrank 	}
219570479a40SFrank 
219670479a40SFrank 	return 0;
219770479a40SFrank }
219870479a40SFrank 
219970479a40SFrank /**
220070479a40SFrank  * yt8521_get_features_paged() -  read supported link modes for one page
220170479a40SFrank  * @phydev: a pointer to a &struct phy_device
220270479a40SFrank  * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
220370479a40SFrank  * operate.
220470479a40SFrank  *
220570479a40SFrank  * returns 0 or negative errno code
220670479a40SFrank  */
yt8521_get_features_paged(struct phy_device * phydev,int page)220770479a40SFrank static int yt8521_get_features_paged(struct phy_device *phydev, int page)
220870479a40SFrank {
220970479a40SFrank 	int old_page;
221070479a40SFrank 	int ret = 0;
221170479a40SFrank 
221270479a40SFrank 	page &= YT8521_RSSR_SPACE_MASK;
221370479a40SFrank 	old_page = phy_select_page(phydev, page);
221470479a40SFrank 	if (old_page < 0)
221570479a40SFrank 		goto err_restore_page;
221670479a40SFrank 
221770479a40SFrank 	if (page == YT8521_RSSR_FIBER_SPACE) {
221870479a40SFrank 		linkmode_zero(phydev->supported);
221970479a40SFrank 		yt8521_prepare_fiber_features(phydev, phydev->supported);
222070479a40SFrank 	} else {
222170479a40SFrank 		ret = ytphy_utp_read_abilities(phydev);
222270479a40SFrank 		if (ret < 0)
222370479a40SFrank 			goto err_restore_page;
222470479a40SFrank 	}
222570479a40SFrank 
222670479a40SFrank err_restore_page:
222770479a40SFrank 	return phy_restore_page(phydev, old_page, ret);
222870479a40SFrank }
222970479a40SFrank 
223070479a40SFrank /**
223170479a40SFrank  * yt8521_get_features - switch reg space then call yt8521_get_features_paged
223270479a40SFrank  * @phydev: target phy_device struct
223370479a40SFrank  *
223470479a40SFrank  * returns 0 or negative errno code
223570479a40SFrank  */
yt8521_get_features(struct phy_device * phydev)223670479a40SFrank static int yt8521_get_features(struct phy_device *phydev)
223770479a40SFrank {
223870479a40SFrank 	struct yt8521_priv *priv = phydev->priv;
223970479a40SFrank 	int ret;
224070479a40SFrank 
224170479a40SFrank 	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
224270479a40SFrank 		ret = yt8521_get_features_paged(phydev, priv->reg_page);
224370479a40SFrank 	} else {
224470479a40SFrank 		ret = yt8521_get_features_paged(phydev,
224570479a40SFrank 						YT8521_RSSR_UTP_SPACE);
224670479a40SFrank 		if (ret < 0)
224770479a40SFrank 			return ret;
224870479a40SFrank 
224970479a40SFrank 		/* add fiber's features to phydev->supported */
225070479a40SFrank 		yt8521_prepare_fiber_features(phydev, phydev->supported);
225170479a40SFrank 	}
225270479a40SFrank 	return ret;
225370479a40SFrank }
225470479a40SFrank 
225548e8c6f1SPeter Geis static struct phy_driver motorcomm_phy_drvs[] = {
225648e8c6f1SPeter Geis 	{
225748e8c6f1SPeter Geis 		PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
225848e8c6f1SPeter Geis 		.name		= "YT8511 Gigabit Ethernet",
225948e8c6f1SPeter Geis 		.config_init	= yt8511_config_init,
226048e8c6f1SPeter Geis 		.suspend	= genphy_suspend,
226148e8c6f1SPeter Geis 		.resume		= genphy_resume,
226248e8c6f1SPeter Geis 		.read_page	= yt8511_read_page,
226348e8c6f1SPeter Geis 		.write_page	= yt8511_write_page,
226448e8c6f1SPeter Geis 	},
226570479a40SFrank 	{
226670479a40SFrank 		PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
226770479a40SFrank 		.name		= "YT8521 Gigabit Ethernet",
226870479a40SFrank 		.get_features	= yt8521_get_features,
226970479a40SFrank 		.probe		= yt8521_probe,
227070479a40SFrank 		.read_page	= yt8521_read_page,
227170479a40SFrank 		.write_page	= yt8521_write_page,
227270479a40SFrank 		.get_wol	= ytphy_get_wol,
227370479a40SFrank 		.set_wol	= ytphy_set_wol,
227470479a40SFrank 		.config_aneg	= yt8521_config_aneg,
227570479a40SFrank 		.aneg_done	= yt8521_aneg_done,
227670479a40SFrank 		.config_init	= yt8521_config_init,
227770479a40SFrank 		.read_status	= yt8521_read_status,
227870479a40SFrank 		.soft_reset	= yt8521_soft_reset,
227970479a40SFrank 		.suspend	= yt8521_suspend,
228070479a40SFrank 		.resume		= yt8521_resume,
228170479a40SFrank 	},
2282813abcd9SFrank 	{
22834ac94f72SFrank Sae 		PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
22844ac94f72SFrank Sae 		.name		= "YT8531 Gigabit Ethernet",
22854ac94f72SFrank Sae 		.probe		= yt8531_probe,
22864ac94f72SFrank Sae 		.config_init	= yt8531_config_init,
22874ac94f72SFrank Sae 		.suspend	= genphy_suspend,
22884ac94f72SFrank Sae 		.resume		= genphy_resume,
22894ac94f72SFrank Sae 		.get_wol	= ytphy_get_wol,
22904ac94f72SFrank Sae 		.set_wol	= yt8531_set_wol,
22914ac94f72SFrank Sae 		.link_change_notify = yt8531_link_change_notify,
22924ac94f72SFrank Sae 	},
22934ac94f72SFrank Sae 	{
2294813abcd9SFrank 		PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
2295813abcd9SFrank 		.name		= "YT8531S Gigabit Ethernet",
2296813abcd9SFrank 		.get_features	= yt8521_get_features,
229736152f87SFrank Sae 		.probe		= yt8521_probe,
2298813abcd9SFrank 		.read_page	= yt8521_read_page,
2299813abcd9SFrank 		.write_page	= yt8521_write_page,
2300813abcd9SFrank 		.get_wol	= ytphy_get_wol,
2301813abcd9SFrank 		.set_wol	= ytphy_set_wol,
2302813abcd9SFrank 		.config_aneg	= yt8521_config_aneg,
2303813abcd9SFrank 		.aneg_done	= yt8521_aneg_done,
2304813abcd9SFrank 		.config_init	= yt8521_config_init,
2305813abcd9SFrank 		.read_status	= yt8521_read_status,
2306813abcd9SFrank 		.soft_reset	= yt8521_soft_reset,
2307813abcd9SFrank 		.suspend	= yt8521_suspend,
2308813abcd9SFrank 		.resume		= yt8521_resume,
2309813abcd9SFrank 	},
231048e8c6f1SPeter Geis };
231148e8c6f1SPeter Geis 
231248e8c6f1SPeter Geis module_phy_driver(motorcomm_phy_drvs);
231348e8c6f1SPeter Geis 
23144ac94f72SFrank Sae MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
231548e8c6f1SPeter Geis MODULE_AUTHOR("Peter Geis");
231670479a40SFrank MODULE_AUTHOR("Frank");
231748e8c6f1SPeter Geis MODULE_LICENSE("GPL");
231848e8c6f1SPeter Geis 
231948e8c6f1SPeter Geis static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
232048e8c6f1SPeter Geis 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
232170479a40SFrank 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
23224ac94f72SFrank Sae 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
2323813abcd9SFrank 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
23244104a713SFrank Sae 	{ /* sentinel */ }
232548e8c6f1SPeter Geis };
232648e8c6f1SPeter Geis 
232748e8c6f1SPeter Geis MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
2328