10
10
#include <linux/mii.h>
11
11
#include <linux/phy.h>
12
12
13
+ #define PHY_ID_ASIX_AX88772A 0x003b1861
14
+ #define PHY_ID_ASIX_AX88772C 0x003b1881
13
15
#define PHY_ID_ASIX_AX88796B 0x003b1841
14
16
15
17
MODULE_DESCRIPTION ("Asix PHY driver" );
@@ -39,7 +41,75 @@ static int asix_soft_reset(struct phy_device *phydev)
39
41
return genphy_soft_reset (phydev );
40
42
}
41
43
42
- static struct phy_driver asix_driver [] = { {
44
+ /* AX88772A is not working properly with some old switches (NETGEAR EN 108TP):
45
+ * after autoneg is done and the link status is reported as active, the MII_LPA
46
+ * register is 0. This issue is not reproducible on AX88772C.
47
+ */
48
+ static int asix_ax88772a_read_status (struct phy_device * phydev )
49
+ {
50
+ int ret , val ;
51
+
52
+ ret = genphy_update_link (phydev );
53
+ if (ret )
54
+ return ret ;
55
+
56
+ if (!phydev -> link )
57
+ return 0 ;
58
+
59
+ /* If MII_LPA is 0, phy_resolve_aneg_linkmode() will fail to resolve
60
+ * linkmode so use MII_BMCR as default values.
61
+ */
62
+ val = phy_read (phydev , MII_BMCR );
63
+ if (val < 0 )
64
+ return val ;
65
+
66
+ if (val & BMCR_SPEED100 )
67
+ phydev -> speed = SPEED_100 ;
68
+ else
69
+ phydev -> speed = SPEED_10 ;
70
+
71
+ if (val & BMCR_FULLDPLX )
72
+ phydev -> duplex = DUPLEX_FULL ;
73
+ else
74
+ phydev -> duplex = DUPLEX_HALF ;
75
+
76
+ ret = genphy_read_lpa (phydev );
77
+ if (ret < 0 )
78
+ return ret ;
79
+
80
+ if (phydev -> autoneg == AUTONEG_ENABLE && phydev -> autoneg_complete )
81
+ phy_resolve_aneg_linkmode (phydev );
82
+
83
+ return 0 ;
84
+ }
85
+
86
+ static void asix_ax88772a_link_change_notify (struct phy_device * phydev )
87
+ {
88
+ /* Reset PHY, otherwise MII_LPA will provide outdated information.
89
+ * This issue is reproducible only with some link partner PHYs
90
+ */
91
+ if (phydev -> state == PHY_NOLINK && phydev -> drv -> soft_reset )
92
+ phydev -> drv -> soft_reset (phydev );
93
+ }
94
+
95
+ static struct phy_driver asix_driver [] = {
96
+ {
97
+ PHY_ID_MATCH_EXACT (PHY_ID_ASIX_AX88772A ),
98
+ .name = "Asix Electronics AX88772A" ,
99
+ .flags = PHY_IS_INTERNAL ,
100
+ .read_status = asix_ax88772a_read_status ,
101
+ .suspend = genphy_suspend ,
102
+ .resume = genphy_resume ,
103
+ .soft_reset = asix_soft_reset ,
104
+ .link_change_notify = asix_ax88772a_link_change_notify ,
105
+ }, {
106
+ PHY_ID_MATCH_EXACT (PHY_ID_ASIX_AX88772C ),
107
+ .name = "Asix Electronics AX88772C" ,
108
+ .flags = PHY_IS_INTERNAL ,
109
+ .suspend = genphy_suspend ,
110
+ .resume = genphy_resume ,
111
+ .soft_reset = asix_soft_reset ,
112
+ }, {
43
113
.phy_id = PHY_ID_ASIX_AX88796B ,
44
114
.name = "Asix Electronics AX88796B" ,
45
115
.phy_id_mask = 0xfffffff0 ,
@@ -50,6 +120,8 @@ static struct phy_driver asix_driver[] = { {
50
120
module_phy_driver (asix_driver );
51
121
52
122
static struct mdio_device_id __maybe_unused asix_tbl [] = {
123
+ { PHY_ID_MATCH_EXACT (PHY_ID_ASIX_AX88772A ) },
124
+ { PHY_ID_MATCH_EXACT (PHY_ID_ASIX_AX88772C ) },
53
125
{ PHY_ID_ASIX_AX88796B , 0xfffffff0 },
54
126
{ }
55
127
};
0 commit comments