1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | #include <linux/bitfield.h> |
3 | #include <linux/module.h> |
4 | #include <linux/phy.h> |
5 | |
6 | #define MTK_EXT_PAGE_ACCESS 0x1f |
7 | #define MTK_PHY_PAGE_STANDARD 0x0000 |
8 | #define MTK_PHY_PAGE_EXTENDED 0x0001 |
9 | #define MTK_PHY_PAGE_EXTENDED_2 0x0002 |
10 | #define MTK_PHY_PAGE_EXTENDED_3 0x0003 |
11 | #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 |
12 | #define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 |
13 | |
14 | static int mtk_gephy_read_page(struct phy_device *phydev) |
15 | { |
16 | return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); |
17 | } |
18 | |
19 | static int mtk_gephy_write_page(struct phy_device *phydev, int page) |
20 | { |
21 | return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, val: page); |
22 | } |
23 | |
24 | static void mtk_gephy_config_init(struct phy_device *phydev) |
25 | { |
26 | /* Disable EEE */ |
27 | phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val: 0); |
28 | |
29 | /* Enable HW auto downshift */ |
30 | phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, regnum: 0x14, mask: 0, BIT(4)); |
31 | |
32 | /* Increase SlvDPSready time */ |
33 | phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); |
34 | __phy_write(phydev, regnum: 0x10, val: 0xafae); |
35 | __phy_write(phydev, regnum: 0x12, val: 0x2f); |
36 | __phy_write(phydev, regnum: 0x10, val: 0x8fae); |
37 | phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, ret: 0); |
38 | |
39 | /* Adjust 100_mse_threshold */ |
40 | phy_write_mmd(phydev, MDIO_MMD_VEND1, regnum: 0x123, val: 0xffff); |
41 | |
42 | /* Disable mcc */ |
43 | phy_write_mmd(phydev, MDIO_MMD_VEND1, regnum: 0xa6, val: 0x300); |
44 | } |
45 | |
46 | static int mt7530_phy_config_init(struct phy_device *phydev) |
47 | { |
48 | mtk_gephy_config_init(phydev); |
49 | |
50 | /* Increase post_update_timer */ |
51 | phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, regnum: 0x11, val: 0x4b); |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static int mt7531_phy_config_init(struct phy_device *phydev) |
57 | { |
58 | mtk_gephy_config_init(phydev); |
59 | |
60 | /* PHY link down power saving enable */ |
61 | phy_set_bits(phydev, regnum: 0x17, BIT(4)); |
62 | phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, regnum: 0xc6, val: 0x300); |
63 | |
64 | /* Set TX Pair delay selection */ |
65 | phy_write_mmd(phydev, MDIO_MMD_VEND1, regnum: 0x13, val: 0x404); |
66 | phy_write_mmd(phydev, MDIO_MMD_VEND1, regnum: 0x14, val: 0x404); |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | static struct phy_driver mtk_gephy_driver[] = { |
72 | { |
73 | PHY_ID_MATCH_EXACT(0x03a29412), |
74 | .name = "MediaTek MT7530 PHY" , |
75 | .config_init = mt7530_phy_config_init, |
76 | /* Interrupts are handled by the switch, not the PHY |
77 | * itself. |
78 | */ |
79 | .config_intr = genphy_no_config_intr, |
80 | .handle_interrupt = genphy_handle_interrupt_no_ack, |
81 | .suspend = genphy_suspend, |
82 | .resume = genphy_resume, |
83 | .read_page = mtk_gephy_read_page, |
84 | .write_page = mtk_gephy_write_page, |
85 | }, |
86 | { |
87 | PHY_ID_MATCH_EXACT(0x03a29441), |
88 | .name = "MediaTek MT7531 PHY" , |
89 | .config_init = mt7531_phy_config_init, |
90 | /* Interrupts are handled by the switch, not the PHY |
91 | * itself. |
92 | */ |
93 | .config_intr = genphy_no_config_intr, |
94 | .handle_interrupt = genphy_handle_interrupt_no_ack, |
95 | .suspend = genphy_suspend, |
96 | .resume = genphy_resume, |
97 | .read_page = mtk_gephy_read_page, |
98 | .write_page = mtk_gephy_write_page, |
99 | }, |
100 | }; |
101 | |
102 | module_phy_driver(mtk_gephy_driver); |
103 | |
104 | static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { |
105 | { PHY_ID_MATCH_EXACT(0x03a29441) }, |
106 | { PHY_ID_MATCH_EXACT(0x03a29412) }, |
107 | { } |
108 | }; |
109 | |
110 | MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver" ); |
111 | MODULE_AUTHOR("DENG, Qingfang <dqfext@gmail.com>" ); |
112 | MODULE_LICENSE("GPL" ); |
113 | |
114 | MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl); |
115 | |