|
14 | 14 | #include <linux/delay.h>
|
15 | 15 | #include <linux/netdevice.h>
|
16 | 16 | #include <linux/etherdevice.h>
|
| 17 | +#include <linux/bitfield.h> |
17 | 18 |
|
18 | 19 | #include <dt-bindings/net/ti-dp83867.h>
|
19 | 20 |
|
20 | 21 | #define DP83867_PHY_ID 0x2000a231
|
21 | 22 | #define DP83867_DEVADDR 0x1f
|
22 | 23 |
|
23 | 24 | #define MII_DP83867_PHYCTRL 0x10
|
| 25 | +#define MII_DP83867_PHYSTS 0x11 |
24 | 26 | #define MII_DP83867_MICR 0x12
|
25 | 27 | #define MII_DP83867_ISR 0x13
|
26 | 28 | #define DP83867_CFG2 0x14
|
|
118 | 120 | #define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8)
|
119 | 121 | #define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8
|
120 | 122 |
|
| 123 | +/* PHY STS bits */ |
| 124 | +#define DP83867_PHYSTS_1000 BIT(15) |
| 125 | +#define DP83867_PHYSTS_100 BIT(14) |
| 126 | +#define DP83867_PHYSTS_DUPLEX BIT(13) |
| 127 | +#define DP83867_PHYSTS_LINK BIT(10) |
| 128 | + |
| 129 | +/* CFG2 bits */ |
| 130 | +#define DP83867_DOWNSHIFT_EN (BIT(8) | BIT(9)) |
| 131 | +#define DP83867_DOWNSHIFT_ATTEMPT_MASK (BIT(10) | BIT(11)) |
| 132 | +#define DP83867_DOWNSHIFT_1_COUNT_VAL 0 |
| 133 | +#define DP83867_DOWNSHIFT_2_COUNT_VAL 1 |
| 134 | +#define DP83867_DOWNSHIFT_4_COUNT_VAL 2 |
| 135 | +#define DP83867_DOWNSHIFT_8_COUNT_VAL 3 |
| 136 | +#define DP83867_DOWNSHIFT_1_COUNT 1 |
| 137 | +#define DP83867_DOWNSHIFT_2_COUNT 2 |
| 138 | +#define DP83867_DOWNSHIFT_4_COUNT 4 |
| 139 | +#define DP83867_DOWNSHIFT_8_COUNT 8 |
| 140 | + |
121 | 141 | /* CFG3 bits */
|
122 | 142 | #define DP83867_CFG3_INT_OE BIT(7)
|
123 | 143 | #define DP83867_CFG3_ROBUST_AUTO_MDIX BIT(9)
|
@@ -287,6 +307,126 @@ static int dp83867_config_intr(struct phy_device *phydev)
|
287 | 307 | return phy_write(phydev, MII_DP83867_MICR, micr_status);
|
288 | 308 | }
|
289 | 309 |
|
| 310 | +static int dp83867_read_status(struct phy_device *phydev) |
| 311 | +{ |
| 312 | + int status = phy_read(phydev, MII_DP83867_PHYSTS); |
| 313 | + int ret; |
| 314 | + |
| 315 | + ret = genphy_read_status(phydev); |
| 316 | + if (ret) |
| 317 | + return ret; |
| 318 | + |
| 319 | + if (status < 0) |
| 320 | + return status; |
| 321 | + |
| 322 | + if (status & DP83867_PHYSTS_DUPLEX) |
| 323 | + phydev->duplex = DUPLEX_FULL; |
| 324 | + else |
| 325 | + phydev->duplex = DUPLEX_HALF; |
| 326 | + |
| 327 | + if (status & DP83867_PHYSTS_1000) |
| 328 | + phydev->speed = SPEED_1000; |
| 329 | + else if (status & DP83867_PHYSTS_100) |
| 330 | + phydev->speed = SPEED_100; |
| 331 | + else |
| 332 | + phydev->speed = SPEED_10; |
| 333 | + |
| 334 | + return 0; |
| 335 | +} |
| 336 | + |
| 337 | +static int dp83867_get_downshift(struct phy_device *phydev, u8 *data) |
| 338 | +{ |
| 339 | + int val, cnt, enable, count; |
| 340 | + |
| 341 | + val = phy_read(phydev, DP83867_CFG2); |
| 342 | + if (val < 0) |
| 343 | + return val; |
| 344 | + |
| 345 | + enable = FIELD_GET(DP83867_DOWNSHIFT_EN, val); |
| 346 | + cnt = FIELD_GET(DP83867_DOWNSHIFT_ATTEMPT_MASK, val); |
| 347 | + |
| 348 | + switch (cnt) { |
| 349 | + case DP83867_DOWNSHIFT_1_COUNT_VAL: |
| 350 | + count = DP83867_DOWNSHIFT_1_COUNT; |
| 351 | + break; |
| 352 | + case DP83867_DOWNSHIFT_2_COUNT_VAL: |
| 353 | + count = DP83867_DOWNSHIFT_2_COUNT; |
| 354 | + break; |
| 355 | + case DP83867_DOWNSHIFT_4_COUNT_VAL: |
| 356 | + count = DP83867_DOWNSHIFT_4_COUNT; |
| 357 | + break; |
| 358 | + case DP83867_DOWNSHIFT_8_COUNT_VAL: |
| 359 | + count = DP83867_DOWNSHIFT_8_COUNT; |
| 360 | + break; |
| 361 | + default: |
| 362 | + return -EINVAL; |
| 363 | + }; |
| 364 | + |
| 365 | + *data = enable ? count : DOWNSHIFT_DEV_DISABLE; |
| 366 | + |
| 367 | + return 0; |
| 368 | +} |
| 369 | + |
| 370 | +static int dp83867_set_downshift(struct phy_device *phydev, u8 cnt) |
| 371 | +{ |
| 372 | + int val, count; |
| 373 | + |
| 374 | + if (cnt > DP83867_DOWNSHIFT_8_COUNT) |
| 375 | + return -E2BIG; |
| 376 | + |
| 377 | + if (!cnt) |
| 378 | + return phy_clear_bits(phydev, DP83867_CFG2, |
| 379 | + DP83867_DOWNSHIFT_EN); |
| 380 | + |
| 381 | + switch (cnt) { |
| 382 | + case DP83867_DOWNSHIFT_1_COUNT: |
| 383 | + count = DP83867_DOWNSHIFT_1_COUNT_VAL; |
| 384 | + break; |
| 385 | + case DP83867_DOWNSHIFT_2_COUNT: |
| 386 | + count = DP83867_DOWNSHIFT_2_COUNT_VAL; |
| 387 | + break; |
| 388 | + case DP83867_DOWNSHIFT_4_COUNT: |
| 389 | + count = DP83867_DOWNSHIFT_4_COUNT_VAL; |
| 390 | + break; |
| 391 | + case DP83867_DOWNSHIFT_8_COUNT: |
| 392 | + count = DP83867_DOWNSHIFT_8_COUNT_VAL; |
| 393 | + break; |
| 394 | + default: |
| 395 | + phydev_err(phydev, |
| 396 | + "Downshift count must be 1, 2, 4 or 8\n"); |
| 397 | + return -EINVAL; |
| 398 | + }; |
| 399 | + |
| 400 | + val = DP83867_DOWNSHIFT_EN; |
| 401 | + val |= FIELD_PREP(DP83867_DOWNSHIFT_ATTEMPT_MASK, count); |
| 402 | + |
| 403 | + return phy_modify(phydev, DP83867_CFG2, |
| 404 | + DP83867_DOWNSHIFT_EN | DP83867_DOWNSHIFT_ATTEMPT_MASK, |
| 405 | + val); |
| 406 | +} |
| 407 | + |
| 408 | +static int dp83867_get_tunable(struct phy_device *phydev, |
| 409 | + struct ethtool_tunable *tuna, void *data) |
| 410 | +{ |
| 411 | + switch (tuna->id) { |
| 412 | + case ETHTOOL_PHY_DOWNSHIFT: |
| 413 | + return dp83867_get_downshift(phydev, data); |
| 414 | + default: |
| 415 | + return -EOPNOTSUPP; |
| 416 | + } |
| 417 | +} |
| 418 | + |
| 419 | +static int dp83867_set_tunable(struct phy_device *phydev, |
| 420 | + struct ethtool_tunable *tuna, const void *data) |
| 421 | +{ |
| 422 | + switch (tuna->id) { |
| 423 | + case ETHTOOL_PHY_DOWNSHIFT: |
| 424 | + return dp83867_set_downshift(phydev, *(const u8 *)data); |
| 425 | + default: |
| 426 | + return -EOPNOTSUPP; |
| 427 | + } |
| 428 | +} |
| 429 | + |
290 | 430 | static int dp83867_config_port_mirroring(struct phy_device *phydev)
|
291 | 431 | {
|
292 | 432 | struct dp83867_private *dp83867 =
|
@@ -467,6 +607,12 @@ static int dp83867_config_init(struct phy_device *phydev)
|
467 | 607 | int ret, val, bs;
|
468 | 608 | u16 delay;
|
469 | 609 |
|
| 610 | + /* Force speed optimization for the PHY even if it strapped */ |
| 611 | + ret = phy_modify(phydev, DP83867_CFG2, DP83867_DOWNSHIFT_EN, |
| 612 | + DP83867_DOWNSHIFT_EN); |
| 613 | + if (ret) |
| 614 | + return ret; |
| 615 | + |
470 | 616 | ret = dp83867_verify_rgmii_cfg(phydev);
|
471 | 617 | if (ret)
|
472 | 618 | return ret;
|
@@ -655,6 +801,10 @@ static struct phy_driver dp83867_driver[] = {
|
655 | 801 | .config_init = dp83867_config_init,
|
656 | 802 | .soft_reset = dp83867_phy_reset,
|
657 | 803 |
|
| 804 | + .read_status = dp83867_read_status, |
| 805 | + .get_tunable = dp83867_get_tunable, |
| 806 | + .set_tunable = dp83867_set_tunable, |
| 807 | + |
658 | 808 | .get_wol = dp83867_get_wol,
|
659 | 809 | .set_wol = dp83867_set_wol,
|
660 | 810 |
|
|
0 commit comments