1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* Driver for Asix PHYs |
3 | * |
4 | * Author: Michael Schmitz <schmitzmic@gmail.com> |
5 | */ |
6 | #include <linux/kernel.h> |
7 | #include <linux/errno.h> |
8 | #include <linux/init.h> |
9 | #include <linux/module.h> |
10 | #include <linux/mii.h> |
11 | #include <linux/phy.h> |
12 | |
13 | #define PHY_ID_ASIX_AX88772A 0x003b1861 |
14 | #define PHY_ID_ASIX_AX88772C 0x003b1881 |
15 | #define PHY_ID_ASIX_AX88796B 0x003b1841 |
16 | |
17 | MODULE_DESCRIPTION("Asix PHY driver" ); |
18 | MODULE_AUTHOR("Michael Schmitz <schmitzmic@gmail.com>" ); |
19 | MODULE_LICENSE("GPL" ); |
20 | |
21 | /** |
22 | * asix_soft_reset - software reset the PHY via BMCR_RESET bit |
23 | * @phydev: target phy_device struct |
24 | * |
25 | * Description: Perform a software PHY reset using the standard |
26 | * BMCR_RESET bit and poll for the reset bit to be cleared. |
27 | * Toggle BMCR_RESET bit off to accommodate broken AX8796B PHY implementation |
28 | * such as used on the Individual Computers' X-Surf 100 Zorro card. |
29 | * |
30 | * Returns: 0 on success, < 0 on failure |
31 | */ |
32 | static int asix_soft_reset(struct phy_device *phydev) |
33 | { |
34 | int ret; |
35 | |
36 | /* Asix PHY won't reset unless reset bit toggles */ |
37 | ret = phy_write(phydev, MII_BMCR, val: 0); |
38 | if (ret < 0) |
39 | return ret; |
40 | |
41 | return genphy_soft_reset(phydev); |
42 | } |
43 | |
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) { |
92 | phy_init_hw(phydev); |
93 | _phy_start_aneg(phydev); |
94 | } |
95 | } |
96 | |
97 | static struct phy_driver asix_driver[] = { |
98 | { |
99 | PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A), |
100 | .name = "Asix Electronics AX88772A" , |
101 | .flags = PHY_IS_INTERNAL, |
102 | .read_status = asix_ax88772a_read_status, |
103 | .suspend = genphy_suspend, |
104 | .resume = genphy_resume, |
105 | .soft_reset = asix_soft_reset, |
106 | .link_change_notify = asix_ax88772a_link_change_notify, |
107 | }, { |
108 | PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C), |
109 | .name = "Asix Electronics AX88772C" , |
110 | .flags = PHY_IS_INTERNAL, |
111 | .suspend = genphy_suspend, |
112 | .resume = genphy_resume, |
113 | .soft_reset = asix_soft_reset, |
114 | }, { |
115 | .phy_id = PHY_ID_ASIX_AX88796B, |
116 | .name = "Asix Electronics AX88796B" , |
117 | .phy_id_mask = 0xfffffff0, |
118 | /* PHY_BASIC_FEATURES */ |
119 | .soft_reset = asix_soft_reset, |
120 | } }; |
121 | |
122 | module_phy_driver(asix_driver); |
123 | |
124 | static struct mdio_device_id __maybe_unused asix_tbl[] = { |
125 | { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) }, |
126 | { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) }, |
127 | { PHY_ID_ASIX_AX88796B, 0xfffffff0 }, |
128 | { } |
129 | }; |
130 | |
131 | MODULE_DEVICE_TABLE(mdio, asix_tbl); |
132 | |