aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/phy/adin.c86
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
257static 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
277static 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
302static 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
313static 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
246static int adin_config_init(struct phy_device *phydev) 324static 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,