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