1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Marvell 88Q2XXX automotive 100BASE-T1/1000BASE-T1 PHY driver |
4 | */ |
5 | #include <linux/ethtool_netlink.h> |
6 | #include <linux/marvell_phy.h> |
7 | #include <linux/phy.h> |
8 | |
9 | #define MDIO_MMD_AN_MV_STAT 32769 |
10 | #define MDIO_MMD_AN_MV_STAT_ANEG 0x0100 |
11 | #define MDIO_MMD_AN_MV_STAT_LOCAL_RX 0x1000 |
12 | #define MDIO_MMD_AN_MV_STAT_REMOTE_RX 0x2000 |
13 | #define MDIO_MMD_AN_MV_STAT_LOCAL_MASTER 0x4000 |
14 | #define MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT 0x8000 |
15 | |
16 | #define MDIO_MMD_PCS_MV_100BT1_STAT1 33032 |
17 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_IDLE_ERROR 0x00FF |
18 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_JABBER 0x0100 |
19 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_LINK 0x0200 |
20 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX 0x1000 |
21 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX 0x2000 |
22 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_MASTER 0x4000 |
23 | |
24 | #define MDIO_MMD_PCS_MV_100BT1_STAT2 33033 |
25 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_JABBER 0x0001 |
26 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_POL 0x0002 |
27 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_LINK 0x0004 |
28 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_ANGE 0x0008 |
29 | |
30 | static int mv88q2xxx_soft_reset(struct phy_device *phydev) |
31 | { |
32 | int ret; |
33 | int val; |
34 | |
35 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, |
36 | MDIO_PCS_1000BT1_CTRL, MDIO_PCS_1000BT1_CTRL_RESET); |
37 | if (ret < 0) |
38 | return ret; |
39 | |
40 | return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS, |
41 | MDIO_PCS_1000BT1_CTRL, val, |
42 | !(val & MDIO_PCS_1000BT1_CTRL_RESET), |
43 | 50000, 600000, true); |
44 | } |
45 | |
46 | static int mv88q2xxx_read_link_gbit(struct phy_device *phydev) |
47 | { |
48 | int ret; |
49 | bool link = false; |
50 | |
51 | /* Read vendor specific Auto-Negotiation status register to get local |
52 | * and remote receiver status according to software initialization |
53 | * guide. |
54 | */ |
55 | ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT); |
56 | if (ret < 0) { |
57 | return ret; |
58 | } else if ((ret & MDIO_MMD_AN_MV_STAT_LOCAL_RX) && |
59 | (ret & MDIO_MMD_AN_MV_STAT_REMOTE_RX)) { |
60 | /* The link state is latched low so that momentary link |
61 | * drops can be detected. Do not double-read the status |
62 | * in polling mode to detect such short link drops except |
63 | * the link was already down. |
64 | */ |
65 | if (!phy_polling_mode(phydev) || !phydev->link) { |
66 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_STAT); |
67 | if (ret < 0) |
68 | return ret; |
69 | else if (ret & MDIO_PCS_1000BT1_STAT_LINK) |
70 | link = true; |
71 | } |
72 | |
73 | if (!link) { |
74 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_STAT); |
75 | if (ret < 0) |
76 | return ret; |
77 | else if (ret & MDIO_PCS_1000BT1_STAT_LINK) |
78 | link = true; |
79 | } |
80 | } |
81 | |
82 | phydev->link = link; |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static int mv88q2xxx_read_link_100m(struct phy_device *phydev) |
88 | { |
89 | int ret; |
90 | |
91 | /* The link state is latched low so that momentary link |
92 | * drops can be detected. Do not double-read the status |
93 | * in polling mode to detect such short link drops except |
94 | * the link was already down. In case we are not polling, |
95 | * we always read the realtime status. |
96 | */ |
97 | if (!phy_polling_mode(phydev) || !phydev->link) { |
98 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1); |
99 | if (ret < 0) |
100 | return ret; |
101 | else if (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) |
102 | goto out; |
103 | } |
104 | |
105 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1); |
106 | if (ret < 0) |
107 | return ret; |
108 | |
109 | out: |
110 | /* Check if we have link and if the remote and local receiver are ok */ |
111 | if ((ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) && |
112 | (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX) && |
113 | (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX)) |
114 | phydev->link = true; |
115 | else |
116 | phydev->link = false; |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static int mv88q2xxx_read_link(struct phy_device *phydev) |
122 | { |
123 | int ret; |
124 | |
125 | /* The 88Q2XXX PHYs do not have the PMA/PMD status register available, |
126 | * therefore we need to read the link status from the vendor specific |
127 | * registers depending on the speed. |
128 | */ |
129 | if (phydev->speed == SPEED_1000) |
130 | ret = mv88q2xxx_read_link_gbit(phydev); |
131 | else |
132 | ret = mv88q2xxx_read_link_100m(phydev); |
133 | |
134 | return ret; |
135 | } |
136 | |
137 | static int mv88q2xxx_read_status(struct phy_device *phydev) |
138 | { |
139 | int ret; |
140 | |
141 | ret = mv88q2xxx_read_link(phydev); |
142 | if (ret < 0) |
143 | return ret; |
144 | |
145 | return genphy_c45_read_pma(phydev); |
146 | } |
147 | |
148 | static int mv88q2xxx_get_features(struct phy_device *phydev) |
149 | { |
150 | int ret; |
151 | |
152 | ret = genphy_c45_pma_read_abilities(phydev); |
153 | if (ret) |
154 | return ret; |
155 | |
156 | /* We need to read the baset1 extended abilities manually because the |
157 | * PHY does not signalize it has the extended abilities register |
158 | * available. |
159 | */ |
160 | ret = genphy_c45_pma_baset1_read_abilities(phydev); |
161 | if (ret) |
162 | return ret; |
163 | |
164 | /* The PHY signalizes it supports autonegotiation. Unfortunately, so |
165 | * far it was not possible to get a link even when following the init |
166 | * sequence provided by Marvell. Disable it for now until a proper |
167 | * workaround is found or a new PHY revision is released. |
168 | */ |
169 | linkmode_clear_bit(nr: ETHTOOL_LINK_MODE_Autoneg_BIT, addr: phydev->supported); |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | static int mv88q2xxx_config_aneg(struct phy_device *phydev) |
175 | { |
176 | int ret; |
177 | |
178 | ret = genphy_c45_config_aneg(phydev); |
179 | if (ret) |
180 | return ret; |
181 | |
182 | return mv88q2xxx_soft_reset(phydev); |
183 | } |
184 | |
185 | static int mv88q2xxx_config_init(struct phy_device *phydev) |
186 | { |
187 | int ret; |
188 | |
189 | /* The 88Q2XXX PHYs do have the extended ability register available, but |
190 | * register MDIO_PMA_EXTABLE where they should signalize it does not |
191 | * work according to specification. Therefore, we force it here. |
192 | */ |
193 | phydev->pma_extable = MDIO_PMA_EXTABLE_BT1; |
194 | |
195 | /* Read the current PHY configuration */ |
196 | ret = genphy_c45_read_pma(phydev); |
197 | if (ret) |
198 | return ret; |
199 | |
200 | return mv88q2xxx_config_aneg(phydev); |
201 | } |
202 | |
203 | static int mv88q2xxxx_get_sqi(struct phy_device *phydev) |
204 | { |
205 | int ret; |
206 | |
207 | if (phydev->speed == SPEED_100) { |
208 | /* Read the SQI from the vendor specific receiver status |
209 | * register |
210 | */ |
211 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, regnum: 0x8230); |
212 | if (ret < 0) |
213 | return ret; |
214 | |
215 | ret = ret >> 12; |
216 | } else { |
217 | /* Read from vendor specific registers, they are not documented |
218 | * but can be found in the Software Initialization Guide. Only |
219 | * revisions >= A0 are supported. |
220 | */ |
221 | ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, regnum: 0xFC5D, mask: 0x00FF, set: 0x00AC); |
222 | if (ret < 0) |
223 | return ret; |
224 | |
225 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, regnum: 0xfc88); |
226 | if (ret < 0) |
227 | return ret; |
228 | } |
229 | |
230 | return ret & 0x0F; |
231 | } |
232 | |
233 | static int mv88q2xxxx_get_sqi_max(struct phy_device *phydev) |
234 | { |
235 | return 15; |
236 | } |
237 | |
238 | static struct phy_driver mv88q2xxx_driver[] = { |
239 | { |
240 | .phy_id = MARVELL_PHY_ID_88Q2110, |
241 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
242 | .name = "mv88q2110" , |
243 | .get_features = mv88q2xxx_get_features, |
244 | .config_aneg = mv88q2xxx_config_aneg, |
245 | .config_init = mv88q2xxx_config_init, |
246 | .read_status = mv88q2xxx_read_status, |
247 | .soft_reset = mv88q2xxx_soft_reset, |
248 | .set_loopback = genphy_c45_loopback, |
249 | .get_sqi = mv88q2xxxx_get_sqi, |
250 | .get_sqi_max = mv88q2xxxx_get_sqi_max, |
251 | }, |
252 | }; |
253 | |
254 | module_phy_driver(mv88q2xxx_driver); |
255 | |
256 | static struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = { |
257 | { MARVELL_PHY_ID_88Q2110, MARVELL_PHY_ID_MASK }, |
258 | { /*sentinel*/ } |
259 | }; |
260 | MODULE_DEVICE_TABLE(mdio, mv88q2xxx_tbl); |
261 | |
262 | MODULE_DESCRIPTION("Marvell 88Q2XXX 100/1000BASE-T1 Automotive Ethernet PHY driver" ); |
263 | MODULE_LICENSE("GPL" ); |
264 | |