diff options
-rw-r--r-- | drivers/net/phy/adin.c | 117 |
1 files changed, 113 insertions, 4 deletions
diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c index 4ca685780622..51c0d17577de 100644 --- a/drivers/net/phy/adin.c +++ b/drivers/net/phy/adin.c | |||
@@ -19,6 +19,10 @@ | |||
19 | #define ADIN1300_MII_EXT_REG_PTR 0x0010 | 19 | #define ADIN1300_MII_EXT_REG_PTR 0x0010 |
20 | #define ADIN1300_MII_EXT_REG_DATA 0x0011 | 20 | #define ADIN1300_MII_EXT_REG_DATA 0x0011 |
21 | 21 | ||
22 | #define ADIN1300_PHY_CTRL1 0x0012 | ||
23 | #define ADIN1300_AUTO_MDI_EN BIT(10) | ||
24 | #define ADIN1300_MAN_MDIX_EN BIT(9) | ||
25 | |||
22 | #define ADIN1300_INT_MASK_REG 0x0018 | 26 | #define ADIN1300_INT_MASK_REG 0x0018 |
23 | #define ADIN1300_INT_MDIO_SYNC_EN BIT(9) | 27 | #define ADIN1300_INT_MDIO_SYNC_EN BIT(9) |
24 | #define ADIN1300_INT_ANEG_STAT_CHNG_EN BIT(8) | 28 | #define ADIN1300_INT_ANEG_STAT_CHNG_EN BIT(8) |
@@ -33,6 +37,9 @@ | |||
33 | (ADIN1300_INT_LINK_STAT_CHNG_EN | ADIN1300_INT_HW_IRQ_EN) | 37 | (ADIN1300_INT_LINK_STAT_CHNG_EN | ADIN1300_INT_HW_IRQ_EN) |
34 | #define ADIN1300_INT_STATUS_REG 0x0019 | 38 | #define ADIN1300_INT_STATUS_REG 0x0019 |
35 | 39 | ||
40 | #define ADIN1300_PHY_STATUS1 0x001a | ||
41 | #define ADIN1300_PAIR_01_SWAP BIT(11) | ||
42 | |||
36 | #define ADIN1300_GE_RGMII_CFG_REG 0xff23 | 43 | #define ADIN1300_GE_RGMII_CFG_REG 0xff23 |
37 | #define ADIN1300_GE_RGMII_RX_MSK GENMASK(8, 6) | 44 | #define ADIN1300_GE_RGMII_RX_MSK GENMASK(8, 6) |
38 | #define ADIN1300_GE_RGMII_RX_SEL(x) \ | 45 | #define ADIN1300_GE_RGMII_RX_SEL(x) \ |
@@ -206,6 +213,8 @@ static int adin_config_init(struct phy_device *phydev) | |||
206 | { | 213 | { |
207 | int rc; | 214 | int rc; |
208 | 215 | ||
216 | phydev->mdix_ctrl = ETH_TP_MDI_AUTO; | ||
217 | |||
209 | rc = genphy_config_init(phydev); | 218 | rc = genphy_config_init(phydev); |
210 | if (rc < 0) | 219 | if (rc < 0) |
211 | return rc; | 220 | return rc; |
@@ -269,13 +278,113 @@ static int adin_write_mmd(struct phy_device *phydev, int devad, u16 regnum, | |||
269 | return __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_DATA, val); | 278 | return __mdiobus_write(bus, phy_addr, ADIN1300_MII_EXT_REG_DATA, val); |
270 | } | 279 | } |
271 | 280 | ||
281 | static int adin_config_mdix(struct phy_device *phydev) | ||
282 | { | ||
283 | bool auto_en, mdix_en; | ||
284 | int reg; | ||
285 | |||
286 | mdix_en = false; | ||
287 | auto_en = false; | ||
288 | switch (phydev->mdix_ctrl) { | ||
289 | case ETH_TP_MDI: | ||
290 | break; | ||
291 | case ETH_TP_MDI_X: | ||
292 | mdix_en = true; | ||
293 | break; | ||
294 | case ETH_TP_MDI_AUTO: | ||
295 | auto_en = true; | ||
296 | break; | ||
297 | default: | ||
298 | return -EINVAL; | ||
299 | } | ||
300 | |||
301 | reg = phy_read(phydev, ADIN1300_PHY_CTRL1); | ||
302 | if (reg < 0) | ||
303 | return reg; | ||
304 | |||
305 | if (mdix_en) | ||
306 | reg |= ADIN1300_MAN_MDIX_EN; | ||
307 | else | ||
308 | reg &= ~ADIN1300_MAN_MDIX_EN; | ||
309 | |||
310 | if (auto_en) | ||
311 | reg |= ADIN1300_AUTO_MDI_EN; | ||
312 | else | ||
313 | reg &= ~ADIN1300_AUTO_MDI_EN; | ||
314 | |||
315 | return phy_write(phydev, ADIN1300_PHY_CTRL1, reg); | ||
316 | } | ||
317 | |||
318 | static int adin_config_aneg(struct phy_device *phydev) | ||
319 | { | ||
320 | int ret; | ||
321 | |||
322 | ret = adin_config_mdix(phydev); | ||
323 | if (ret) | ||
324 | return ret; | ||
325 | |||
326 | return genphy_config_aneg(phydev); | ||
327 | } | ||
328 | |||
329 | static int adin_mdix_update(struct phy_device *phydev) | ||
330 | { | ||
331 | bool auto_en, mdix_en; | ||
332 | bool swapped; | ||
333 | int reg; | ||
334 | |||
335 | reg = phy_read(phydev, ADIN1300_PHY_CTRL1); | ||
336 | if (reg < 0) | ||
337 | return reg; | ||
338 | |||
339 | auto_en = !!(reg & ADIN1300_AUTO_MDI_EN); | ||
340 | mdix_en = !!(reg & ADIN1300_MAN_MDIX_EN); | ||
341 | |||
342 | /* If MDI/MDIX is forced, just read it from the control reg */ | ||
343 | if (!auto_en) { | ||
344 | if (mdix_en) | ||
345 | phydev->mdix = ETH_TP_MDI_X; | ||
346 | else | ||
347 | phydev->mdix = ETH_TP_MDI; | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | /** | ||
352 | * Otherwise, we need to deduce it from the PHY status2 reg. | ||
353 | * When Auto-MDI is enabled, the ADIN1300_MAN_MDIX_EN bit implies | ||
354 | * a preference for MDIX when it is set. | ||
355 | */ | ||
356 | reg = phy_read(phydev, ADIN1300_PHY_STATUS1); | ||
357 | if (reg < 0) | ||
358 | return reg; | ||
359 | |||
360 | swapped = !!(reg & ADIN1300_PAIR_01_SWAP); | ||
361 | |||
362 | if (mdix_en != swapped) | ||
363 | phydev->mdix = ETH_TP_MDI_X; | ||
364 | else | ||
365 | phydev->mdix = ETH_TP_MDI; | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static int adin_read_status(struct phy_device *phydev) | ||
371 | { | ||
372 | int ret; | ||
373 | |||
374 | ret = adin_mdix_update(phydev); | ||
375 | if (ret < 0) | ||
376 | return ret; | ||
377 | |||
378 | return genphy_read_status(phydev); | ||
379 | } | ||
380 | |||
272 | static struct phy_driver adin_driver[] = { | 381 | static struct phy_driver adin_driver[] = { |
273 | { | 382 | { |
274 | PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200), | 383 | PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200), |
275 | .name = "ADIN1200", | 384 | .name = "ADIN1200", |
276 | .config_init = adin_config_init, | 385 | .config_init = adin_config_init, |
277 | .config_aneg = genphy_config_aneg, | 386 | .config_aneg = adin_config_aneg, |
278 | .read_status = genphy_read_status, | 387 | .read_status = adin_read_status, |
279 | .ack_interrupt = adin_phy_ack_intr, | 388 | .ack_interrupt = adin_phy_ack_intr, |
280 | .config_intr = adin_phy_config_intr, | 389 | .config_intr = adin_phy_config_intr, |
281 | .resume = genphy_resume, | 390 | .resume = genphy_resume, |
@@ -287,8 +396,8 @@ static struct phy_driver adin_driver[] = { | |||
287 | PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300), | 396 | PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300), |
288 | .name = "ADIN1300", | 397 | .name = "ADIN1300", |
289 | .config_init = adin_config_init, | 398 | .config_init = adin_config_init, |
290 | .config_aneg = genphy_config_aneg, | 399 | .config_aneg = adin_config_aneg, |
291 | .read_status = genphy_read_status, | 400 | .read_status = adin_read_status, |
292 | .ack_interrupt = adin_phy_ack_intr, | 401 | .ack_interrupt = adin_phy_ack_intr, |
293 | .config_intr = adin_phy_config_intr, | 402 | .config_intr = adin_phy_config_intr, |
294 | .resume = genphy_resume, | 403 | .resume = genphy_resume, |