]>
Commit | Line | Data |
---|---|---|
a2443fd1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
20b2af32 RK |
2 | /* |
3 | * Marvell 10G 88x3310 PHY driver | |
4 | * | |
5 | * Based upon the ID registers, this PHY appears to be a mixture of IPs | |
6 | * from two different companies. | |
7 | * | |
8 | * There appears to be several different data paths through the PHY which | |
9 | * are automatically managed by the PHY. The following has been determined | |
05ca1b32 | 10 | * via observation and experimentation for a setup using single-lane Serdes: |
20b2af32 RK |
11 | * |
12 | * SGMII PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for <= 1G) | |
13 | * 10GBASE-KR PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for 10G) | |
14 | * 10GBASE-KR PHYXS -- BASE-R PCS -- Fiber | |
15 | * | |
05ca1b32 RK |
16 | * With XAUI, observation shows: |
17 | * | |
18 | * XAUI PHYXS -- <appropriate PCS as above> | |
19 | * | |
20 | * and no switching of the host interface mode occurs. | |
21 | * | |
20b2af32 RK |
22 | * If both the fiber and copper ports are connected, the first to gain |
23 | * link takes priority and the other port is completely locked out. | |
24 | */ | |
0d3ad854 | 25 | #include <linux/ctype.h> |
8d8963c3 | 26 | #include <linux/delay.h> |
0d3ad854 | 27 | #include <linux/hwmon.h> |
952b6b3b | 28 | #include <linux/marvell_phy.h> |
0d3ad854 | 29 | #include <linux/phy.h> |
36023da1 | 30 | #include <linux/sfp.h> |
20b2af32 | 31 | |
c47455f9 MC |
32 | #define MV_PHY_ALASKA_NBT_QUIRK_MASK 0xfffffffe |
33 | #define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa) | |
34 | ||
20b2af32 | 35 | enum { |
dd649b4f RK |
36 | MV_PMA_FW_VER0 = 0xc011, |
37 | MV_PMA_FW_VER1 = 0xc012, | |
3d3ced2e RK |
38 | MV_PMA_BOOT = 0xc050, |
39 | MV_PMA_BOOT_FATAL = BIT(0), | |
40 | ||
20b2af32 RK |
41 | MV_PCS_BASE_T = 0x0000, |
42 | MV_PCS_BASE_R = 0x1000, | |
43 | MV_PCS_1000BASEX = 0x2000, | |
44 | ||
8d8963c3 | 45 | MV_PCS_CSCR1 = 0x8000, |
a585c03e RK |
46 | MV_PCS_CSCR1_ED_MASK = 0x0300, |
47 | MV_PCS_CSCR1_ED_OFF = 0x0000, | |
48 | MV_PCS_CSCR1_ED_RX = 0x0200, | |
49 | MV_PCS_CSCR1_ED_NLP = 0x0300, | |
8d8963c3 RK |
50 | MV_PCS_CSCR1_MDIX_MASK = 0x0060, |
51 | MV_PCS_CSCR1_MDIX_MDI = 0x0000, | |
52 | MV_PCS_CSCR1_MDIX_MDIX = 0x0020, | |
53 | MV_PCS_CSCR1_MDIX_AUTO = 0x0060, | |
54 | ||
c84786fa RK |
55 | MV_PCS_CSSR1 = 0x8008, |
56 | MV_PCS_CSSR1_SPD1_MASK = 0xc000, | |
57 | MV_PCS_CSSR1_SPD1_SPD2 = 0xc000, | |
58 | MV_PCS_CSSR1_SPD1_1000 = 0x8000, | |
59 | MV_PCS_CSSR1_SPD1_100 = 0x4000, | |
60 | MV_PCS_CSSR1_SPD1_10 = 0x0000, | |
61 | MV_PCS_CSSR1_DUPLEX_FULL= BIT(13), | |
62 | MV_PCS_CSSR1_RESOLVED = BIT(11), | |
63 | MV_PCS_CSSR1_MDIX = BIT(6), | |
64 | MV_PCS_CSSR1_SPD2_MASK = 0x000c, | |
65 | MV_PCS_CSSR1_SPD2_5000 = 0x0008, | |
66 | MV_PCS_CSSR1_SPD2_2500 = 0x0004, | |
67 | MV_PCS_CSSR1_SPD2_10000 = 0x0000, | |
ea4efe25 | 68 | |
20b2af32 RK |
69 | /* These registers appear at 0x800X and 0xa00X - the 0xa00X control |
70 | * registers appear to set themselves to the 0x800X when AN is | |
71 | * restarted, but status registers appear readable from either. | |
72 | */ | |
73 | MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */ | |
74 | MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */ | |
0d3ad854 RK |
75 | |
76 | /* Vendor2 MMD registers */ | |
af3e28cb | 77 | MV_V2_PORT_CTRL = 0xf001, |
8f48c2ac RK |
78 | MV_V2_PORT_CTRL_SWRST = BIT(15), |
79 | MV_V2_PORT_CTRL_PWRDOWN = BIT(11), | |
0d3ad854 RK |
80 | MV_V2_TEMP_CTRL = 0xf08a, |
81 | MV_V2_TEMP_CTRL_MASK = 0xc000, | |
82 | MV_V2_TEMP_CTRL_SAMPLE = 0x0000, | |
83 | MV_V2_TEMP_CTRL_DISABLE = 0xc000, | |
84 | MV_V2_TEMP = 0xf08c, | |
85 | MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */ | |
86 | }; | |
87 | ||
88 | struct mv3310_priv { | |
dd649b4f RK |
89 | u32 firmware_ver; |
90 | ||
0d3ad854 RK |
91 | struct device *hwmon_dev; |
92 | char *hwmon_name; | |
20b2af32 RK |
93 | }; |
94 | ||
0d3ad854 RK |
95 | #ifdef CONFIG_HWMON |
96 | static umode_t mv3310_hwmon_is_visible(const void *data, | |
97 | enum hwmon_sensor_types type, | |
98 | u32 attr, int channel) | |
99 | { | |
100 | if (type == hwmon_chip && attr == hwmon_chip_update_interval) | |
101 | return 0444; | |
102 | if (type == hwmon_temp && attr == hwmon_temp_input) | |
103 | return 0444; | |
104 | return 0; | |
105 | } | |
106 | ||
107 | static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type, | |
108 | u32 attr, int channel, long *value) | |
109 | { | |
110 | struct phy_device *phydev = dev_get_drvdata(dev); | |
111 | int temp; | |
112 | ||
113 | if (type == hwmon_chip && attr == hwmon_chip_update_interval) { | |
114 | *value = MSEC_PER_SEC; | |
115 | return 0; | |
116 | } | |
117 | ||
118 | if (type == hwmon_temp && attr == hwmon_temp_input) { | |
119 | temp = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP); | |
120 | if (temp < 0) | |
121 | return temp; | |
122 | ||
123 | *value = ((temp & 0xff) - 75) * 1000; | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | return -EOPNOTSUPP; | |
129 | } | |
130 | ||
131 | static const struct hwmon_ops mv3310_hwmon_ops = { | |
132 | .is_visible = mv3310_hwmon_is_visible, | |
133 | .read = mv3310_hwmon_read, | |
134 | }; | |
135 | ||
136 | static u32 mv3310_hwmon_chip_config[] = { | |
137 | HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL, | |
138 | 0, | |
139 | }; | |
140 | ||
141 | static const struct hwmon_channel_info mv3310_hwmon_chip = { | |
142 | .type = hwmon_chip, | |
143 | .config = mv3310_hwmon_chip_config, | |
144 | }; | |
145 | ||
146 | static u32 mv3310_hwmon_temp_config[] = { | |
147 | HWMON_T_INPUT, | |
148 | 0, | |
149 | }; | |
150 | ||
151 | static const struct hwmon_channel_info mv3310_hwmon_temp = { | |
152 | .type = hwmon_temp, | |
153 | .config = mv3310_hwmon_temp_config, | |
154 | }; | |
155 | ||
156 | static const struct hwmon_channel_info *mv3310_hwmon_info[] = { | |
157 | &mv3310_hwmon_chip, | |
158 | &mv3310_hwmon_temp, | |
159 | NULL, | |
160 | }; | |
161 | ||
162 | static const struct hwmon_chip_info mv3310_hwmon_chip_info = { | |
163 | .ops = &mv3310_hwmon_ops, | |
164 | .info = mv3310_hwmon_info, | |
165 | }; | |
166 | ||
167 | static int mv3310_hwmon_config(struct phy_device *phydev, bool enable) | |
168 | { | |
169 | u16 val; | |
170 | int ret; | |
171 | ||
172 | ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP, | |
173 | MV_V2_TEMP_UNKNOWN); | |
174 | if (ret < 0) | |
175 | return ret; | |
176 | ||
177 | val = enable ? MV_V2_TEMP_CTRL_SAMPLE : MV_V2_TEMP_CTRL_DISABLE; | |
0d3ad854 | 178 | |
b06d8e5a HK |
179 | return phy_modify_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL, |
180 | MV_V2_TEMP_CTRL_MASK, val); | |
0d3ad854 RK |
181 | } |
182 | ||
183 | static void mv3310_hwmon_disable(void *data) | |
184 | { | |
185 | struct phy_device *phydev = data; | |
186 | ||
187 | mv3310_hwmon_config(phydev, false); | |
188 | } | |
189 | ||
190 | static int mv3310_hwmon_probe(struct phy_device *phydev) | |
191 | { | |
192 | struct device *dev = &phydev->mdio.dev; | |
193 | struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); | |
194 | int i, j, ret; | |
195 | ||
196 | priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); | |
197 | if (!priv->hwmon_name) | |
198 | return -ENODEV; | |
199 | ||
200 | for (i = j = 0; priv->hwmon_name[i]; i++) { | |
201 | if (isalnum(priv->hwmon_name[i])) { | |
202 | if (i != j) | |
203 | priv->hwmon_name[j] = priv->hwmon_name[i]; | |
204 | j++; | |
205 | } | |
206 | } | |
207 | priv->hwmon_name[j] = '\0'; | |
208 | ||
209 | ret = mv3310_hwmon_config(phydev, true); | |
210 | if (ret) | |
211 | return ret; | |
212 | ||
213 | ret = devm_add_action_or_reset(dev, mv3310_hwmon_disable, phydev); | |
214 | if (ret) | |
215 | return ret; | |
216 | ||
217 | priv->hwmon_dev = devm_hwmon_device_register_with_info(dev, | |
218 | priv->hwmon_name, phydev, | |
219 | &mv3310_hwmon_chip_info, NULL); | |
220 | ||
221 | return PTR_ERR_OR_ZERO(priv->hwmon_dev); | |
222 | } | |
223 | #else | |
224 | static inline int mv3310_hwmon_config(struct phy_device *phydev, bool enable) | |
225 | { | |
226 | return 0; | |
227 | } | |
228 | ||
229 | static int mv3310_hwmon_probe(struct phy_device *phydev) | |
230 | { | |
231 | return 0; | |
232 | } | |
233 | #endif | |
234 | ||
c9cc1c81 RK |
235 | static int mv3310_power_down(struct phy_device *phydev) |
236 | { | |
237 | return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, | |
238 | MV_V2_PORT_CTRL_PWRDOWN); | |
239 | } | |
240 | ||
241 | static int mv3310_power_up(struct phy_device *phydev) | |
242 | { | |
8f48c2ac RK |
243 | struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); |
244 | int ret; | |
245 | ||
246 | ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, | |
247 | MV_V2_PORT_CTRL_PWRDOWN); | |
248 | ||
829e7573 BS |
249 | if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310 || |
250 | priv->firmware_ver < 0x00030000) | |
8f48c2ac RK |
251 | return ret; |
252 | ||
253 | return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, | |
254 | MV_V2_PORT_CTRL_SWRST); | |
c9cc1c81 RK |
255 | } |
256 | ||
8d8963c3 RK |
257 | static int mv3310_reset(struct phy_device *phydev, u32 unit) |
258 | { | |
8964a217 | 259 | int val, err; |
8d8963c3 RK |
260 | |
261 | err = phy_modify_mmd(phydev, MDIO_MMD_PCS, unit + MDIO_CTRL1, | |
262 | MDIO_CTRL1_RESET, MDIO_CTRL1_RESET); | |
263 | if (err < 0) | |
264 | return err; | |
265 | ||
8964a217 DZ |
266 | return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS, |
267 | unit + MDIO_CTRL1, val, | |
268 | !(val & MDIO_CTRL1_RESET), | |
269 | 5000, 100000, true); | |
8d8963c3 RK |
270 | } |
271 | ||
a585c03e RK |
272 | static int mv3310_get_edpd(struct phy_device *phydev, u16 *edpd) |
273 | { | |
274 | int val; | |
275 | ||
276 | val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1); | |
277 | if (val < 0) | |
278 | return val; | |
279 | ||
280 | switch (val & MV_PCS_CSCR1_ED_MASK) { | |
281 | case MV_PCS_CSCR1_ED_NLP: | |
282 | *edpd = 1000; | |
283 | break; | |
284 | case MV_PCS_CSCR1_ED_RX: | |
285 | *edpd = ETHTOOL_PHY_EDPD_NO_TX; | |
286 | break; | |
287 | default: | |
288 | *edpd = ETHTOOL_PHY_EDPD_DISABLE; | |
289 | break; | |
290 | } | |
291 | return 0; | |
292 | } | |
293 | ||
294 | static int mv3310_set_edpd(struct phy_device *phydev, u16 edpd) | |
295 | { | |
296 | u16 val; | |
297 | int err; | |
298 | ||
299 | switch (edpd) { | |
300 | case 1000: | |
301 | case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: | |
302 | val = MV_PCS_CSCR1_ED_NLP; | |
303 | break; | |
304 | ||
305 | case ETHTOOL_PHY_EDPD_NO_TX: | |
306 | val = MV_PCS_CSCR1_ED_RX; | |
307 | break; | |
308 | ||
309 | case ETHTOOL_PHY_EDPD_DISABLE: | |
310 | val = MV_PCS_CSCR1_ED_OFF; | |
311 | break; | |
312 | ||
313 | default: | |
314 | return -EINVAL; | |
315 | } | |
316 | ||
317 | err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1, | |
318 | MV_PCS_CSCR1_ED_MASK, val); | |
319 | if (err > 0) | |
320 | err = mv3310_reset(phydev, MV_PCS_BASE_T); | |
321 | ||
322 | return err; | |
323 | } | |
324 | ||
36023da1 RK |
325 | static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) |
326 | { | |
327 | struct phy_device *phydev = upstream; | |
328 | __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; | |
329 | phy_interface_t iface; | |
330 | ||
331 | sfp_parse_support(phydev->sfp_bus, id, support); | |
a4516c70 | 332 | iface = sfp_select_interface(phydev->sfp_bus, support); |
36023da1 | 333 | |
e0f909bc | 334 | if (iface != PHY_INTERFACE_MODE_10GBASER) { |
36023da1 RK |
335 | dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); |
336 | return -EINVAL; | |
337 | } | |
338 | return 0; | |
339 | } | |
340 | ||
341 | static const struct sfp_upstream_ops mv3310_sfp_ops = { | |
342 | .attach = phy_sfp_attach, | |
343 | .detach = phy_sfp_detach, | |
344 | .module_insert = mv3310_sfp_insert, | |
345 | }; | |
346 | ||
20b2af32 RK |
347 | static int mv3310_probe(struct phy_device *phydev) |
348 | { | |
0d3ad854 | 349 | struct mv3310_priv *priv; |
20b2af32 | 350 | u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; |
0d3ad854 | 351 | int ret; |
20b2af32 RK |
352 | |
353 | if (!phydev->is_c45 || | |
354 | (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) | |
355 | return -ENODEV; | |
356 | ||
3d3ced2e RK |
357 | ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT); |
358 | if (ret < 0) | |
359 | return ret; | |
360 | ||
361 | if (ret & MV_PMA_BOOT_FATAL) { | |
362 | dev_warn(&phydev->mdio.dev, | |
363 | "PHY failed to boot firmware, status=%04x\n", ret); | |
364 | return -ENODEV; | |
365 | } | |
366 | ||
0d3ad854 RK |
367 | priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); |
368 | if (!priv) | |
369 | return -ENOMEM; | |
370 | ||
371 | dev_set_drvdata(&phydev->mdio.dev, priv); | |
372 | ||
dd649b4f RK |
373 | ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); |
374 | if (ret < 0) | |
375 | return ret; | |
376 | ||
377 | priv->firmware_ver = ret << 16; | |
378 | ||
379 | ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER1); | |
380 | if (ret < 0) | |
381 | return ret; | |
382 | ||
383 | priv->firmware_ver |= ret; | |
384 | ||
385 | phydev_info(phydev, "Firmware version %u.%u.%u.%u\n", | |
386 | priv->firmware_ver >> 24, (priv->firmware_ver >> 16) & 255, | |
387 | (priv->firmware_ver >> 8) & 255, priv->firmware_ver & 255); | |
388 | ||
c9cc1c81 RK |
389 | /* Powering down the port when not in use saves about 600mW */ |
390 | ret = mv3310_power_down(phydev); | |
391 | if (ret) | |
392 | return ret; | |
393 | ||
0d3ad854 RK |
394 | ret = mv3310_hwmon_probe(phydev); |
395 | if (ret) | |
396 | return ret; | |
397 | ||
36023da1 | 398 | return phy_sfp_probe(phydev, &mv3310_sfp_ops); |
0d3ad854 RK |
399 | } |
400 | ||
401 | static int mv3310_suspend(struct phy_device *phydev) | |
402 | { | |
c9cc1c81 | 403 | return mv3310_power_down(phydev); |
20b2af32 RK |
404 | } |
405 | ||
0d3ad854 RK |
406 | static int mv3310_resume(struct phy_device *phydev) |
407 | { | |
af3e28cb AT |
408 | int ret; |
409 | ||
c9cc1c81 | 410 | ret = mv3310_power_up(phydev); |
af3e28cb AT |
411 | if (ret) |
412 | return ret; | |
413 | ||
0d3ad854 RK |
414 | return mv3310_hwmon_config(phydev, true); |
415 | } | |
416 | ||
c47455f9 MC |
417 | /* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010 |
418 | * don't set bit 14 in PMA Extended Abilities (1.11), although they do | |
419 | * support 2.5GBASET and 5GBASET. For these models, we can still read their | |
420 | * 2.5G/5G extended abilities register (1.21). We detect these models based on | |
421 | * the PMA device identifier, with a mask matching models known to have this | |
422 | * issue | |
423 | */ | |
424 | static bool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev) | |
425 | { | |
426 | if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_PMAPMD)) | |
427 | return false; | |
428 | ||
429 | /* Only some revisions of the 88X3310 family PMA seem to be impacted */ | |
430 | return (phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & | |
431 | MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV; | |
432 | } | |
433 | ||
20b2af32 RK |
434 | static int mv3310_config_init(struct phy_device *phydev) |
435 | { | |
c9cc1c81 RK |
436 | int err; |
437 | ||
20b2af32 RK |
438 | /* Check that the PHY interface type is compatible */ |
439 | if (phydev->interface != PHY_INTERFACE_MODE_SGMII && | |
e555e5b1 | 440 | phydev->interface != PHY_INTERFACE_MODE_2500BASEX && |
20b2af32 RK |
441 | phydev->interface != PHY_INTERFACE_MODE_XAUI && |
442 | phydev->interface != PHY_INTERFACE_MODE_RXAUI && | |
e0f909bc | 443 | phydev->interface != PHY_INTERFACE_MODE_10GBASER) |
20b2af32 RK |
444 | return -ENODEV; |
445 | ||
8d8963c3 RK |
446 | phydev->mdix_ctrl = ETH_TP_MDI_AUTO; |
447 | ||
c9cc1c81 RK |
448 | /* Power up so reset works */ |
449 | err = mv3310_power_up(phydev); | |
450 | if (err) | |
451 | return err; | |
452 | ||
a585c03e RK |
453 | /* Enable EDPD mode - saving 600mW */ |
454 | return mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); | |
74145424 MC |
455 | } |
456 | ||
457 | static int mv3310_get_features(struct phy_device *phydev) | |
458 | { | |
459 | int ret, val; | |
460 | ||
ac3f5533 MC |
461 | ret = genphy_c45_pma_read_abilities(phydev); |
462 | if (ret) | |
463 | return ret; | |
20b2af32 | 464 | |
c47455f9 MC |
465 | if (mv3310_has_pma_ngbaset_quirk(phydev)) { |
466 | val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, | |
467 | MDIO_PMA_NG_EXTABLE); | |
468 | if (val < 0) | |
469 | return val; | |
470 | ||
471 | linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, | |
472 | phydev->supported, | |
473 | val & MDIO_PMA_NG_EXTABLE_2_5GBT); | |
474 | ||
475 | linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, | |
476 | phydev->supported, | |
477 | val & MDIO_PMA_NG_EXTABLE_5GBT); | |
478 | } | |
479 | ||
20b2af32 RK |
480 | return 0; |
481 | } | |
482 | ||
8d8963c3 RK |
483 | static int mv3310_config_mdix(struct phy_device *phydev) |
484 | { | |
485 | u16 val; | |
486 | int err; | |
487 | ||
488 | switch (phydev->mdix_ctrl) { | |
489 | case ETH_TP_MDI_AUTO: | |
490 | val = MV_PCS_CSCR1_MDIX_AUTO; | |
491 | break; | |
492 | case ETH_TP_MDI_X: | |
493 | val = MV_PCS_CSCR1_MDIX_MDIX; | |
494 | break; | |
495 | case ETH_TP_MDI: | |
496 | val = MV_PCS_CSCR1_MDIX_MDI; | |
497 | break; | |
498 | default: | |
499 | return -EINVAL; | |
500 | } | |
501 | ||
502 | err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1, | |
503 | MV_PCS_CSCR1_MDIX_MASK, val); | |
504 | if (err > 0) | |
505 | err = mv3310_reset(phydev, MV_PCS_BASE_T); | |
506 | ||
507 | return err; | |
508 | } | |
509 | ||
20b2af32 RK |
510 | static int mv3310_config_aneg(struct phy_device *phydev) |
511 | { | |
512 | bool changed = false; | |
3c1bcc86 | 513 | u16 reg; |
20b2af32 RK |
514 | int ret; |
515 | ||
8d8963c3 RK |
516 | ret = mv3310_config_mdix(phydev); |
517 | if (ret < 0) | |
518 | return ret; | |
ea4efe25 | 519 | |
30de65c3 HK |
520 | if (phydev->autoneg == AUTONEG_DISABLE) |
521 | return genphy_c45_pma_setup_forced(phydev); | |
20b2af32 | 522 | |
3de97f3c | 523 | ret = genphy_c45_an_config_aneg(phydev); |
20b2af32 RK |
524 | if (ret < 0) |
525 | return ret; | |
526 | if (ret > 0) | |
527 | changed = true; | |
528 | ||
3de97f3c AL |
529 | /* Clause 45 has no standardized support for 1000BaseT, therefore |
530 | * use vendor registers for this mode. | |
531 | */ | |
3c1bcc86 | 532 | reg = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); |
b06d8e5a | 533 | ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MV_AN_CTRL1000, |
b52c018d | 534 | ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg); |
20b2af32 RK |
535 | if (ret < 0) |
536 | return ret; | |
537 | if (ret > 0) | |
538 | changed = true; | |
539 | ||
6b4cb6cb | 540 | return genphy_c45_check_and_restart_aneg(phydev, changed); |
20b2af32 RK |
541 | } |
542 | ||
543 | static int mv3310_aneg_done(struct phy_device *phydev) | |
544 | { | |
545 | int val; | |
546 | ||
547 | val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1); | |
548 | if (val < 0) | |
549 | return val; | |
550 | ||
551 | if (val & MDIO_STAT1_LSTATUS) | |
552 | return 1; | |
553 | ||
554 | return genphy_c45_aneg_done(phydev); | |
555 | } | |
556 | ||
36c4449a RK |
557 | static void mv3310_update_interface(struct phy_device *phydev) |
558 | { | |
559 | if ((phydev->interface == PHY_INTERFACE_MODE_SGMII || | |
e555e5b1 | 560 | phydev->interface == PHY_INTERFACE_MODE_2500BASEX || |
e0f909bc RK |
561 | phydev->interface == PHY_INTERFACE_MODE_10GBASER) && |
562 | phydev->link) { | |
36c4449a | 563 | /* The PHY automatically switches its serdes interface (and |
e0f909bc | 564 | * active PHYXS instance) between Cisco SGMII, 10GBase-R and |
e555e5b1 MC |
565 | * 2500BaseX modes according to the speed. Florian suggests |
566 | * setting phydev->interface to communicate this to the MAC. | |
567 | * Only do this if we are already in one of the above modes. | |
36c4449a | 568 | */ |
e555e5b1 MC |
569 | switch (phydev->speed) { |
570 | case SPEED_10000: | |
e0f909bc | 571 | phydev->interface = PHY_INTERFACE_MODE_10GBASER; |
e555e5b1 MC |
572 | break; |
573 | case SPEED_2500: | |
574 | phydev->interface = PHY_INTERFACE_MODE_2500BASEX; | |
575 | break; | |
576 | case SPEED_1000: | |
577 | case SPEED_100: | |
578 | case SPEED_10: | |
36c4449a | 579 | phydev->interface = PHY_INTERFACE_MODE_SGMII; |
e555e5b1 MC |
580 | break; |
581 | default: | |
582 | break; | |
583 | } | |
36c4449a RK |
584 | } |
585 | } | |
586 | ||
20b2af32 | 587 | /* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */ |
c84786fa | 588 | static int mv3310_read_status_10gbaser(struct phy_device *phydev) |
20b2af32 RK |
589 | { |
590 | phydev->link = 1; | |
591 | phydev->speed = SPEED_10000; | |
592 | phydev->duplex = DUPLEX_FULL; | |
593 | ||
20b2af32 RK |
594 | return 0; |
595 | } | |
596 | ||
c84786fa | 597 | static int mv3310_read_status_copper(struct phy_device *phydev) |
20b2af32 | 598 | { |
c84786fa | 599 | int cssr1, speed, val; |
20b2af32 | 600 | |
998a8a83 | 601 | val = genphy_c45_read_link(phydev); |
20b2af32 RK |
602 | if (val < 0) |
603 | return val; | |
604 | ||
20b2af32 RK |
605 | val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); |
606 | if (val < 0) | |
607 | return val; | |
608 | ||
c84786fa RK |
609 | cssr1 = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSSR1); |
610 | if (cssr1 < 0) | |
611 | return val; | |
612 | ||
613 | /* If the link settings are not resolved, mark the link down */ | |
614 | if (!(cssr1 & MV_PCS_CSSR1_RESOLVED)) { | |
615 | phydev->link = 0; | |
616 | return 0; | |
617 | } | |
618 | ||
619 | /* Read the copper link settings */ | |
620 | speed = cssr1 & MV_PCS_CSSR1_SPD1_MASK; | |
621 | if (speed == MV_PCS_CSSR1_SPD1_SPD2) | |
622 | speed |= cssr1 & MV_PCS_CSSR1_SPD2_MASK; | |
623 | ||
624 | switch (speed) { | |
625 | case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_10000: | |
626 | phydev->speed = SPEED_10000; | |
627 | break; | |
628 | ||
629 | case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_5000: | |
630 | phydev->speed = SPEED_5000; | |
631 | break; | |
632 | ||
633 | case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_2500: | |
634 | phydev->speed = SPEED_2500; | |
635 | break; | |
636 | ||
637 | case MV_PCS_CSSR1_SPD1_1000: | |
638 | phydev->speed = SPEED_1000; | |
639 | break; | |
640 | ||
641 | case MV_PCS_CSSR1_SPD1_100: | |
642 | phydev->speed = SPEED_100; | |
643 | break; | |
644 | ||
645 | case MV_PCS_CSSR1_SPD1_10: | |
646 | phydev->speed = SPEED_10; | |
647 | break; | |
648 | } | |
649 | ||
650 | phydev->duplex = cssr1 & MV_PCS_CSSR1_DUPLEX_FULL ? | |
651 | DUPLEX_FULL : DUPLEX_HALF; | |
652 | phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ? | |
653 | ETH_TP_MDI_X : ETH_TP_MDI; | |
654 | ||
20b2af32 RK |
655 | if (val & MDIO_AN_STAT1_COMPLETE) { |
656 | val = genphy_c45_read_lpa(phydev); | |
657 | if (val < 0) | |
658 | return val; | |
659 | ||
cc1122b0 | 660 | /* Read the link partner's 1G advertisement */ |
20b2af32 RK |
661 | val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_STAT1000); |
662 | if (val < 0) | |
663 | return val; | |
664 | ||
78a24df3 | 665 | mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); |
20b2af32 | 666 | |
c84786fa RK |
667 | /* Update the pause status */ |
668 | phy_resolve_aneg_pause(phydev); | |
20b2af32 RK |
669 | } |
670 | ||
c84786fa RK |
671 | return 0; |
672 | } | |
20b2af32 | 673 | |
c84786fa RK |
674 | static int mv3310_read_status(struct phy_device *phydev) |
675 | { | |
676 | int err, val; | |
ea4efe25 | 677 | |
c84786fa RK |
678 | phydev->speed = SPEED_UNKNOWN; |
679 | phydev->duplex = DUPLEX_UNKNOWN; | |
680 | linkmode_zero(phydev->lp_advertising); | |
681 | phydev->link = 0; | |
682 | phydev->pause = 0; | |
683 | phydev->asym_pause = 0; | |
684 | phydev->mdix = ETH_TP_MDI_INVALID; | |
ea4efe25 | 685 | |
c84786fa RK |
686 | val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1); |
687 | if (val < 0) | |
688 | return val; | |
689 | ||
690 | if (val & MDIO_STAT1_LSTATUS) | |
691 | err = mv3310_read_status_10gbaser(phydev); | |
692 | else | |
693 | err = mv3310_read_status_copper(phydev); | |
694 | if (err < 0) | |
695 | return err; | |
696 | ||
697 | if (phydev->link) | |
698 | mv3310_update_interface(phydev); | |
20b2af32 RK |
699 | |
700 | return 0; | |
701 | } | |
702 | ||
a585c03e RK |
703 | static int mv3310_get_tunable(struct phy_device *phydev, |
704 | struct ethtool_tunable *tuna, void *data) | |
705 | { | |
706 | switch (tuna->id) { | |
707 | case ETHTOOL_PHY_EDPD: | |
708 | return mv3310_get_edpd(phydev, data); | |
709 | default: | |
710 | return -EOPNOTSUPP; | |
711 | } | |
712 | } | |
713 | ||
714 | static int mv3310_set_tunable(struct phy_device *phydev, | |
715 | struct ethtool_tunable *tuna, const void *data) | |
716 | { | |
717 | switch (tuna->id) { | |
718 | case ETHTOOL_PHY_EDPD: | |
719 | return mv3310_set_edpd(phydev, *(u16 *)data); | |
720 | default: | |
721 | return -EOPNOTSUPP; | |
722 | } | |
723 | } | |
724 | ||
20b2af32 RK |
725 | static struct phy_driver mv3310_drivers[] = { |
726 | { | |
631ba906 | 727 | .phy_id = MARVELL_PHY_ID_88X3310, |
952b6b3b | 728 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
20b2af32 | 729 | .name = "mv88x3310", |
74145424 | 730 | .get_features = mv3310_get_features, |
7be3ad84 | 731 | .soft_reset = genphy_no_soft_reset, |
20b2af32 | 732 | .config_init = mv3310_config_init, |
0d3ad854 RK |
733 | .probe = mv3310_probe, |
734 | .suspend = mv3310_suspend, | |
735 | .resume = mv3310_resume, | |
20b2af32 RK |
736 | .config_aneg = mv3310_config_aneg, |
737 | .aneg_done = mv3310_aneg_done, | |
738 | .read_status = mv3310_read_status, | |
a585c03e RK |
739 | .get_tunable = mv3310_get_tunable, |
740 | .set_tunable = mv3310_set_tunable, | |
20b2af32 | 741 | }, |
62d01535 MC |
742 | { |
743 | .phy_id = MARVELL_PHY_ID_88E2110, | |
744 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
745 | .name = "mv88x2110", | |
62d01535 | 746 | .probe = mv3310_probe, |
e02c4a9d AT |
747 | .suspend = mv3310_suspend, |
748 | .resume = mv3310_resume, | |
7be3ad84 | 749 | .soft_reset = genphy_no_soft_reset, |
62d01535 MC |
750 | .config_init = mv3310_config_init, |
751 | .config_aneg = mv3310_config_aneg, | |
752 | .aneg_done = mv3310_aneg_done, | |
753 | .read_status = mv3310_read_status, | |
a585c03e RK |
754 | .get_tunable = mv3310_get_tunable, |
755 | .set_tunable = mv3310_set_tunable, | |
62d01535 | 756 | }, |
20b2af32 RK |
757 | }; |
758 | ||
759 | module_phy_driver(mv3310_drivers); | |
760 | ||
761 | static struct mdio_device_id __maybe_unused mv3310_tbl[] = { | |
631ba906 | 762 | { MARVELL_PHY_ID_88X3310, MARVELL_PHY_ID_MASK }, |
62d01535 | 763 | { MARVELL_PHY_ID_88E2110, MARVELL_PHY_ID_MASK }, |
20b2af32 RK |
764 | { }, |
765 | }; | |
766 | MODULE_DEVICE_TABLE(mdio, mv3310_tbl); | |
767 | MODULE_DESCRIPTION("Marvell Alaska X 10Gigabit Ethernet PHY driver (MV88X3310)"); | |
768 | MODULE_LICENSE("GPL"); |