|  | // SPDX-License-Identifier: GPL-2.0+ | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/phy.h> | 
|  |  | 
|  | #define MTK_EXT_PAGE_ACCESS		0x1f | 
|  | #define MTK_PHY_PAGE_STANDARD		0x0000 | 
|  | #define MTK_PHY_PAGE_EXTENDED		0x0001 | 
|  | #define MTK_PHY_PAGE_EXTENDED_2		0x0002 | 
|  | #define MTK_PHY_PAGE_EXTENDED_3		0x0003 | 
|  | #define MTK_PHY_PAGE_EXTENDED_2A30	0x2a30 | 
|  | #define MTK_PHY_PAGE_EXTENDED_52B5	0x52b5 | 
|  |  | 
|  | static int mtk_gephy_read_page(struct phy_device *phydev) | 
|  | { | 
|  | return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); | 
|  | } | 
|  |  | 
|  | static int mtk_gephy_write_page(struct phy_device *phydev, int page) | 
|  | { | 
|  | return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); | 
|  | } | 
|  |  | 
|  | static void mtk_gephy_config_init(struct phy_device *phydev) | 
|  | { | 
|  | /* Disable EEE */ | 
|  | phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); | 
|  |  | 
|  | /* Enable HW auto downshift */ | 
|  | phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); | 
|  |  | 
|  | /* Increase SlvDPSready time */ | 
|  | phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); | 
|  | __phy_write(phydev, 0x10, 0xafae); | 
|  | __phy_write(phydev, 0x12, 0x2f); | 
|  | __phy_write(phydev, 0x10, 0x8fae); | 
|  | phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); | 
|  |  | 
|  | /* Adjust 100_mse_threshold */ | 
|  | phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); | 
|  |  | 
|  | /* Disable mcc */ | 
|  | phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); | 
|  | } | 
|  |  | 
|  | static int mt7530_phy_config_init(struct phy_device *phydev) | 
|  | { | 
|  | mtk_gephy_config_init(phydev); | 
|  |  | 
|  | /* Increase post_update_timer */ | 
|  | phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int mt7531_phy_config_init(struct phy_device *phydev) | 
|  | { | 
|  | mtk_gephy_config_init(phydev); | 
|  |  | 
|  | /* PHY link down power saving enable */ | 
|  | phy_set_bits(phydev, 0x17, BIT(4)); | 
|  | phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); | 
|  |  | 
|  | /* Set TX Pair delay selection */ | 
|  | phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); | 
|  | phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct phy_driver mtk_gephy_driver[] = { | 
|  | { | 
|  | PHY_ID_MATCH_EXACT(0x03a29412), | 
|  | .name		= "MediaTek MT7530 PHY", | 
|  | .config_init	= mt7530_phy_config_init, | 
|  | /* Interrupts are handled by the switch, not the PHY | 
|  | * itself. | 
|  | */ | 
|  | .config_intr	= genphy_no_config_intr, | 
|  | .handle_interrupt = genphy_handle_interrupt_no_ack, | 
|  | .suspend	= genphy_suspend, | 
|  | .resume		= genphy_resume, | 
|  | .read_page	= mtk_gephy_read_page, | 
|  | .write_page	= mtk_gephy_write_page, | 
|  | }, | 
|  | { | 
|  | PHY_ID_MATCH_EXACT(0x03a29441), | 
|  | .name		= "MediaTek MT7531 PHY", | 
|  | .config_init	= mt7531_phy_config_init, | 
|  | /* Interrupts are handled by the switch, not the PHY | 
|  | * itself. | 
|  | */ | 
|  | .config_intr	= genphy_no_config_intr, | 
|  | .handle_interrupt = genphy_handle_interrupt_no_ack, | 
|  | .suspend	= genphy_suspend, | 
|  | .resume		= genphy_resume, | 
|  | .read_page	= mtk_gephy_read_page, | 
|  | .write_page	= mtk_gephy_write_page, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | module_phy_driver(mtk_gephy_driver); | 
|  |  | 
|  | static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { | 
|  | { PHY_ID_MATCH_VENDOR(0x03a29400) }, | 
|  | { } | 
|  | }; | 
|  |  | 
|  | MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); | 
|  | MODULE_AUTHOR("DENG, Qingfang <dqfext@gmail.com>"); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  | 
|  | MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl); |