diff options
-rw-r--r-- | drivers/net/phy/adin.c | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c index 5622a393e7cf..131b577a17e5 100644 --- a/drivers/net/phy/adin.c +++ b/drivers/net/phy/adin.c | |||
@@ -24,6 +24,17 @@ | |||
24 | #define ADIN1300_AUTO_MDI_EN BIT(10) | 24 | #define ADIN1300_AUTO_MDI_EN BIT(10) |
25 | #define ADIN1300_MAN_MDIX_EN BIT(9) | 25 | #define ADIN1300_MAN_MDIX_EN BIT(9) |
26 | 26 | ||
27 | #define ADIN1300_PHY_CTRL2 0x0016 | ||
28 | #define ADIN1300_DOWNSPEED_AN_100_EN BIT(11) | ||
29 | #define ADIN1300_DOWNSPEED_AN_10_EN BIT(10) | ||
30 | #define ADIN1300_GROUP_MDIO_EN BIT(6) | ||
31 | #define ADIN1300_DOWNSPEEDS_EN \ | ||
32 | (ADIN1300_DOWNSPEED_AN_100_EN | ADIN1300_DOWNSPEED_AN_10_EN) | ||
33 | |||
34 | #define ADIN1300_PHY_CTRL3 0x0017 | ||
35 | #define ADIN1300_LINKING_EN BIT(13) | ||
36 | #define ADIN1300_DOWNSPEED_RETRIES_MSK GENMASK(12, 10) | ||
37 | |||
27 | #define ADIN1300_INT_MASK_REG 0x0018 | 38 | #define ADIN1300_INT_MASK_REG 0x0018 |
28 | #define ADIN1300_INT_MDIO_SYNC_EN BIT(9) | 39 | #define ADIN1300_INT_MDIO_SYNC_EN BIT(9) |
29 | #define ADIN1300_INT_ANEG_STAT_CHNG_EN BIT(8) | 40 | #define ADIN1300_INT_ANEG_STAT_CHNG_EN BIT(8) |
@@ -243,6 +254,73 @@ static int adin_config_rmii_mode(struct phy_device *phydev) | |||
243 | ADIN1300_GE_RMII_CFG_REG, reg); | 254 | ADIN1300_GE_RMII_CFG_REG, reg); |
244 | } | 255 | } |
245 | 256 | ||
257 | static int adin_get_downshift(struct phy_device *phydev, u8 *data) | ||
258 | { | ||
259 | int val, cnt, enable; | ||
260 | |||
261 | val = phy_read(phydev, ADIN1300_PHY_CTRL2); | ||
262 | if (val < 0) | ||
263 | return val; | ||
264 | |||
265 | cnt = phy_read(phydev, ADIN1300_PHY_CTRL3); | ||
266 | if (cnt < 0) | ||
267 | return cnt; | ||
268 | |||
269 | enable = FIELD_GET(ADIN1300_DOWNSPEEDS_EN, val); | ||
270 | cnt = FIELD_GET(ADIN1300_DOWNSPEED_RETRIES_MSK, cnt); | ||
271 | |||
272 | *data = (enable && cnt) ? cnt : DOWNSHIFT_DEV_DISABLE; | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int adin_set_downshift(struct phy_device *phydev, u8 cnt) | ||
278 | { | ||
279 | u16 val; | ||
280 | int rc; | ||
281 | |||
282 | if (cnt == DOWNSHIFT_DEV_DISABLE) | ||
283 | return phy_clear_bits(phydev, ADIN1300_PHY_CTRL2, | ||
284 | ADIN1300_DOWNSPEEDS_EN); | ||
285 | |||
286 | if (cnt > 7) | ||
287 | return -E2BIG; | ||
288 | |||
289 | val = FIELD_PREP(ADIN1300_DOWNSPEED_RETRIES_MSK, cnt); | ||
290 | val |= ADIN1300_LINKING_EN; | ||
291 | |||
292 | rc = phy_modify(phydev, ADIN1300_PHY_CTRL3, | ||
293 | ADIN1300_LINKING_EN | ADIN1300_DOWNSPEED_RETRIES_MSK, | ||
294 | val); | ||
295 | if (rc < 0) | ||
296 | return rc; | ||
297 | |||
298 | return phy_set_bits(phydev, ADIN1300_PHY_CTRL2, | ||
299 | ADIN1300_DOWNSPEEDS_EN); | ||
300 | } | ||
301 | |||
302 | static int adin_get_tunable(struct phy_device *phydev, | ||
303 | struct ethtool_tunable *tuna, void *data) | ||
304 | { | ||
305 | switch (tuna->id) { | ||
306 | case ETHTOOL_PHY_DOWNSHIFT: | ||
307 | return adin_get_downshift(phydev, data); | ||
308 | default: | ||
309 | return -EOPNOTSUPP; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | static int adin_set_tunable(struct phy_device *phydev, | ||
314 | struct ethtool_tunable *tuna, const void *data) | ||
315 | { | ||
316 | switch (tuna->id) { | ||
317 | case ETHTOOL_PHY_DOWNSHIFT: | ||
318 | return adin_set_downshift(phydev, *(const u8 *)data); | ||
319 | default: | ||
320 | return -EOPNOTSUPP; | ||
321 | } | ||
322 | } | ||
323 | |||
246 | static int adin_config_init(struct phy_device *phydev) | 324 | static int adin_config_init(struct phy_device *phydev) |
247 | { | 325 | { |
248 | int rc; | 326 | int rc; |
@@ -261,6 +339,10 @@ static int adin_config_init(struct phy_device *phydev) | |||
261 | if (rc < 0) | 339 | if (rc < 0) |
262 | return rc; | 340 | return rc; |
263 | 341 | ||
342 | rc = adin_set_downshift(phydev, 4); | ||
343 | if (rc < 0) | ||
344 | return rc; | ||
345 | |||
264 | phydev_dbg(phydev, "PHY is using mode '%s'\n", | 346 | phydev_dbg(phydev, "PHY is using mode '%s'\n", |
265 | phy_modes(phydev->interface)); | 347 | phy_modes(phydev->interface)); |
266 | 348 | ||
@@ -474,6 +556,8 @@ static struct phy_driver adin_driver[] = { | |||
474 | .soft_reset = adin_soft_reset, | 556 | .soft_reset = adin_soft_reset, |
475 | .config_aneg = adin_config_aneg, | 557 | .config_aneg = adin_config_aneg, |
476 | .read_status = adin_read_status, | 558 | .read_status = adin_read_status, |
559 | .get_tunable = adin_get_tunable, | ||
560 | .set_tunable = adin_set_tunable, | ||
477 | .ack_interrupt = adin_phy_ack_intr, | 561 | .ack_interrupt = adin_phy_ack_intr, |
478 | .config_intr = adin_phy_config_intr, | 562 | .config_intr = adin_phy_config_intr, |
479 | .resume = genphy_resume, | 563 | .resume = genphy_resume, |
@@ -488,6 +572,8 @@ static struct phy_driver adin_driver[] = { | |||
488 | .soft_reset = adin_soft_reset, | 572 | .soft_reset = adin_soft_reset, |
489 | .config_aneg = adin_config_aneg, | 573 | .config_aneg = adin_config_aneg, |
490 | .read_status = adin_read_status, | 574 | .read_status = adin_read_status, |
575 | .get_tunable = adin_get_tunable, | ||
576 | .set_tunable = adin_set_tunable, | ||
491 | .ack_interrupt = adin_phy_ack_intr, | 577 | .ack_interrupt = adin_phy_ack_intr, |
492 | .config_intr = adin_phy_config_intr, | 578 | .config_intr = adin_phy_config_intr, |
493 | .resume = genphy_resume, | 579 | .resume = genphy_resume, |