| // 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); |