| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Broadcom BCM7xxx internal transceivers support. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2014-2017 Broadcom |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public License |
|---|
| 8 | | - * as published by the Free Software Foundation; either version |
|---|
| 9 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 15 | 11 | #include "bcm-phy-lib.h" |
|---|
| 16 | 12 | #include <linux/bitops.h> |
|---|
| 17 | 13 | #include <linux/brcmphy.h> |
|---|
| 14 | +#include <linux/clk.h> |
|---|
| 18 | 15 | #include <linux/mdio.h> |
|---|
| 19 | 16 | |
|---|
| 20 | 17 | /* Broadcom BCM7xxx internal PHY registers */ |
|---|
| .. | .. |
|---|
| 46 | 43 | #define MII_BCM7XXX_SHD_3_TL4 0x23 |
|---|
| 47 | 44 | #define MII_BCM7XXX_TL4_RST_MSK (BIT(2) | BIT(1)) |
|---|
| 48 | 45 | |
|---|
| 49 | | -/* 28nm only register definitions */ |
|---|
| 50 | | -#define MISC_ADDR(base, channel) base, channel |
|---|
| 51 | | - |
|---|
| 52 | | -#define DSP_TAP10 MISC_ADDR(0x0a, 0) |
|---|
| 53 | | -#define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1) |
|---|
| 54 | | -#define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2) |
|---|
| 55 | | -#define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0) |
|---|
| 56 | | - |
|---|
| 57 | | -#define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0) |
|---|
| 58 | | -#define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1) |
|---|
| 59 | | -#define AFE_RXCONFIG_2 MISC_ADDR(0x38, 2) |
|---|
| 60 | | -#define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3) |
|---|
| 61 | | -#define AFE_TX_CONFIG MISC_ADDR(0x39, 0) |
|---|
| 62 | | -#define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1) |
|---|
| 63 | | -#define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) |
|---|
| 64 | | -#define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) |
|---|
| 65 | | - |
|---|
| 66 | 46 | struct bcm7xxx_phy_priv { |
|---|
| 67 | 47 | u64 *stats; |
|---|
| 48 | + struct clk *clk; |
|---|
| 68 | 49 | }; |
|---|
| 69 | | - |
|---|
| 70 | | -static void r_rc_cal_reset(struct phy_device *phydev) |
|---|
| 71 | | -{ |
|---|
| 72 | | - /* Reset R_CAL/RC_CAL Engine */ |
|---|
| 73 | | - bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); |
|---|
| 74 | | - |
|---|
| 75 | | - /* Disable Reset R_AL/RC_CAL Engine */ |
|---|
| 76 | | - bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); |
|---|
| 77 | | -} |
|---|
| 78 | | - |
|---|
| 79 | | -static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) |
|---|
| 80 | | -{ |
|---|
| 81 | | - /* Increase VCO range to prevent unlocking problem of PLL at low |
|---|
| 82 | | - * temp |
|---|
| 83 | | - */ |
|---|
| 84 | | - bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); |
|---|
| 85 | | - |
|---|
| 86 | | - /* Change Ki to 011 */ |
|---|
| 87 | | - bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); |
|---|
| 88 | | - |
|---|
| 89 | | - /* Disable loading of TVCO buffer to bandgap, set bandgap trim |
|---|
| 90 | | - * to 111 |
|---|
| 91 | | - */ |
|---|
| 92 | | - bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); |
|---|
| 93 | | - |
|---|
| 94 | | - /* Adjust bias current trim by -3 */ |
|---|
| 95 | | - bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); |
|---|
| 96 | | - |
|---|
| 97 | | - /* Switch to CORE_BASE1E */ |
|---|
| 98 | | - phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); |
|---|
| 99 | | - |
|---|
| 100 | | - r_rc_cal_reset(phydev); |
|---|
| 101 | | - |
|---|
| 102 | | - /* write AFE_RXCONFIG_0 */ |
|---|
| 103 | | - bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); |
|---|
| 104 | | - |
|---|
| 105 | | - /* write AFE_RXCONFIG_1 */ |
|---|
| 106 | | - bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); |
|---|
| 107 | | - |
|---|
| 108 | | - /* write AFE_RX_LP_COUNTER */ |
|---|
| 109 | | - bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); |
|---|
| 110 | | - |
|---|
| 111 | | - /* write AFE_HPF_TRIM_OTHERS */ |
|---|
| 112 | | - bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); |
|---|
| 113 | | - |
|---|
| 114 | | - /* write AFTE_TX_CONFIG */ |
|---|
| 115 | | - bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); |
|---|
| 116 | | - |
|---|
| 117 | | - return 0; |
|---|
| 118 | | -} |
|---|
| 119 | 50 | |
|---|
| 120 | 51 | static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) |
|---|
| 121 | 52 | { |
|---|
| .. | .. |
|---|
| 152 | 83 | bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); |
|---|
| 153 | 84 | |
|---|
| 154 | 85 | /* Reset R_CAL/RC_CAL engine */ |
|---|
| 155 | | - r_rc_cal_reset(phydev); |
|---|
| 86 | + bcm_phy_r_rc_cal_reset(phydev); |
|---|
| 156 | 87 | |
|---|
| 157 | 88 | return 0; |
|---|
| 158 | 89 | } |
|---|
| .. | .. |
|---|
| 180 | 111 | bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); |
|---|
| 181 | 112 | |
|---|
| 182 | 113 | /* Reset R_CAL/RC_CAL engine */ |
|---|
| 183 | | - r_rc_cal_reset(phydev); |
|---|
| 114 | + bcm_phy_r_rc_cal_reset(phydev); |
|---|
| 184 | 115 | |
|---|
| 185 | 116 | return 0; |
|---|
| 186 | 117 | } |
|---|
| .. | .. |
|---|
| 205 | 136 | /* Enable ffe zero detection for Vitesse interoperability */ |
|---|
| 206 | 137 | bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015); |
|---|
| 207 | 138 | |
|---|
| 208 | | - r_rc_cal_reset(phydev); |
|---|
| 139 | + bcm_phy_r_rc_cal_reset(phydev); |
|---|
| 209 | 140 | |
|---|
| 210 | 141 | return 0; |
|---|
| 211 | 142 | } |
|---|
| .. | .. |
|---|
| 236 | 167 | switch (rev) { |
|---|
| 237 | 168 | case 0xa0: |
|---|
| 238 | 169 | case 0xb0: |
|---|
| 239 | | - ret = bcm7xxx_28nm_b0_afe_config_init(phydev); |
|---|
| 170 | + ret = bcm_phy_28nm_a0b0_afe_config_init(phydev); |
|---|
| 240 | 171 | break; |
|---|
| 241 | 172 | case 0xd0: |
|---|
| 242 | 173 | ret = bcm7xxx_28nm_d0_afe_config_init(phydev); |
|---|
| .. | .. |
|---|
| 254 | 185 | break; |
|---|
| 255 | 186 | } |
|---|
| 256 | 187 | |
|---|
| 188 | + if (ret) |
|---|
| 189 | + return ret; |
|---|
| 190 | + |
|---|
| 191 | + ret = bcm_phy_enable_jumbo(phydev); |
|---|
| 257 | 192 | if (ret) |
|---|
| 258 | 193 | return ret; |
|---|
| 259 | 194 | |
|---|
| .. | .. |
|---|
| 286 | 221 | return genphy_config_aneg(phydev); |
|---|
| 287 | 222 | } |
|---|
| 288 | 223 | |
|---|
| 289 | | -static int phy_set_clr_bits(struct phy_device *dev, int location, |
|---|
| 290 | | - int set_mask, int clr_mask) |
|---|
| 224 | +static int __phy_set_clr_bits(struct phy_device *dev, int location, |
|---|
| 225 | + int set_mask, int clr_mask) |
|---|
| 291 | 226 | { |
|---|
| 292 | 227 | int v, ret; |
|---|
| 293 | 228 | |
|---|
| 294 | | - v = phy_read(dev, location); |
|---|
| 229 | + v = __phy_read(dev, location); |
|---|
| 295 | 230 | if (v < 0) |
|---|
| 296 | 231 | return v; |
|---|
| 297 | 232 | |
|---|
| 298 | 233 | v &= ~clr_mask; |
|---|
| 299 | 234 | v |= set_mask; |
|---|
| 300 | 235 | |
|---|
| 301 | | - ret = phy_write(dev, location, v); |
|---|
| 236 | + ret = __phy_write(dev, location, v); |
|---|
| 302 | 237 | if (ret < 0) |
|---|
| 303 | 238 | return ret; |
|---|
| 304 | 239 | |
|---|
| 305 | 240 | return v; |
|---|
| 241 | +} |
|---|
| 242 | + |
|---|
| 243 | +static int phy_set_clr_bits(struct phy_device *dev, int location, |
|---|
| 244 | + int set_mask, int clr_mask) |
|---|
| 245 | +{ |
|---|
| 246 | + int ret; |
|---|
| 247 | + |
|---|
| 248 | + mutex_lock(&dev->mdio.bus->mdio_lock); |
|---|
| 249 | + ret = __phy_set_clr_bits(dev, location, set_mask, clr_mask); |
|---|
| 250 | + mutex_unlock(&dev->mdio.bus->mdio_lock); |
|---|
| 251 | + |
|---|
| 252 | + return ret; |
|---|
| 306 | 253 | } |
|---|
| 307 | 254 | |
|---|
| 308 | 255 | static int bcm7xxx_28nm_ephy_01_afe_config_init(struct phy_device *phydev) |
|---|
| .. | .. |
|---|
| 506 | 453 | return -EOPNOTSUPP; |
|---|
| 507 | 454 | |
|---|
| 508 | 455 | /* set shadow mode 2 */ |
|---|
| 509 | | - ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, |
|---|
| 510 | | - MII_BCM7XXX_SHD_MODE_2, 0); |
|---|
| 456 | + ret = __phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, |
|---|
| 457 | + MII_BCM7XXX_SHD_MODE_2, 0); |
|---|
| 511 | 458 | if (ret < 0) |
|---|
| 512 | 459 | return ret; |
|---|
| 513 | 460 | |
|---|
| 514 | 461 | /* Access the desired shadow register address */ |
|---|
| 515 | | - ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, shd); |
|---|
| 462 | + ret = __phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, shd); |
|---|
| 516 | 463 | if (ret < 0) |
|---|
| 517 | 464 | goto reset_shadow_mode; |
|---|
| 518 | 465 | |
|---|
| 519 | | - ret = phy_read(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT); |
|---|
| 466 | + ret = __phy_read(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT); |
|---|
| 520 | 467 | |
|---|
| 521 | 468 | reset_shadow_mode: |
|---|
| 522 | 469 | /* reset shadow mode 2 */ |
|---|
| 523 | | - phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, |
|---|
| 524 | | - MII_BCM7XXX_SHD_MODE_2); |
|---|
| 470 | + __phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, |
|---|
| 471 | + MII_BCM7XXX_SHD_MODE_2); |
|---|
| 525 | 472 | return ret; |
|---|
| 526 | 473 | } |
|---|
| 527 | 474 | |
|---|
| .. | .. |
|---|
| 536 | 483 | return -EOPNOTSUPP; |
|---|
| 537 | 484 | |
|---|
| 538 | 485 | /* set shadow mode 2 */ |
|---|
| 539 | | - ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, |
|---|
| 540 | | - MII_BCM7XXX_SHD_MODE_2, 0); |
|---|
| 486 | + ret = __phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, |
|---|
| 487 | + MII_BCM7XXX_SHD_MODE_2, 0); |
|---|
| 541 | 488 | if (ret < 0) |
|---|
| 542 | 489 | return ret; |
|---|
| 543 | 490 | |
|---|
| 544 | 491 | /* Access the desired shadow register address */ |
|---|
| 545 | | - ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, shd); |
|---|
| 492 | + ret = __phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, shd); |
|---|
| 546 | 493 | if (ret < 0) |
|---|
| 547 | 494 | goto reset_shadow_mode; |
|---|
| 548 | 495 | |
|---|
| 549 | 496 | /* Write the desired value in the shadow register */ |
|---|
| 550 | | - phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, val); |
|---|
| 497 | + __phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, val); |
|---|
| 551 | 498 | |
|---|
| 552 | 499 | reset_shadow_mode: |
|---|
| 553 | 500 | /* reset shadow mode 2 */ |
|---|
| 554 | | - return phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, |
|---|
| 555 | | - MII_BCM7XXX_SHD_MODE_2); |
|---|
| 501 | + return __phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, |
|---|
| 502 | + MII_BCM7XXX_SHD_MODE_2); |
|---|
| 556 | 503 | } |
|---|
| 557 | 504 | |
|---|
| 558 | 505 | static int bcm7xxx_28nm_ephy_resume(struct phy_device *phydev) |
|---|
| .. | .. |
|---|
| 680 | 627 | static int bcm7xxx_28nm_probe(struct phy_device *phydev) |
|---|
| 681 | 628 | { |
|---|
| 682 | 629 | struct bcm7xxx_phy_priv *priv; |
|---|
| 630 | + int ret = 0; |
|---|
| 683 | 631 | |
|---|
| 684 | 632 | priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); |
|---|
| 685 | 633 | if (!priv) |
|---|
| .. | .. |
|---|
| 693 | 641 | if (!priv->stats) |
|---|
| 694 | 642 | return -ENOMEM; |
|---|
| 695 | 643 | |
|---|
| 696 | | - return 0; |
|---|
| 644 | + priv->clk = devm_clk_get_optional(&phydev->mdio.dev, NULL); |
|---|
| 645 | + if (IS_ERR(priv->clk)) |
|---|
| 646 | + return PTR_ERR(priv->clk); |
|---|
| 647 | + |
|---|
| 648 | + ret = clk_prepare_enable(priv->clk); |
|---|
| 649 | + if (ret) |
|---|
| 650 | + return ret; |
|---|
| 651 | + |
|---|
| 652 | + /* Dummy read to a register to workaround an issue upon reset where the |
|---|
| 653 | + * internal inverter may not allow the first MDIO transaction to pass |
|---|
| 654 | + * the MDIO management controller and make us return 0xffff for such |
|---|
| 655 | + * reads. This is needed to ensure that any subsequent reads to the |
|---|
| 656 | + * PHY will succeed. |
|---|
| 657 | + */ |
|---|
| 658 | + phy_read(phydev, MII_BMSR); |
|---|
| 659 | + |
|---|
| 660 | + return ret; |
|---|
| 661 | +} |
|---|
| 662 | + |
|---|
| 663 | +static void bcm7xxx_28nm_remove(struct phy_device *phydev) |
|---|
| 664 | +{ |
|---|
| 665 | + struct bcm7xxx_phy_priv *priv = phydev->priv; |
|---|
| 666 | + |
|---|
| 667 | + clk_disable_unprepare(priv->clk); |
|---|
| 697 | 668 | } |
|---|
| 698 | 669 | |
|---|
| 699 | 670 | #define BCM7XXX_28NM_GPHY(_oui, _name) \ |
|---|
| .. | .. |
|---|
| 701 | 672 | .phy_id = (_oui), \ |
|---|
| 702 | 673 | .phy_id_mask = 0xfffffff0, \ |
|---|
| 703 | 674 | .name = _name, \ |
|---|
| 704 | | - .features = PHY_GBIT_FEATURES, \ |
|---|
| 675 | + /* PHY_GBIT_FEATURES */ \ |
|---|
| 705 | 676 | .flags = PHY_IS_INTERNAL, \ |
|---|
| 706 | 677 | .config_init = bcm7xxx_28nm_config_init, \ |
|---|
| 707 | 678 | .resume = bcm7xxx_28nm_resume, \ |
|---|
| .. | .. |
|---|
| 711 | 682 | .get_strings = bcm_phy_get_strings, \ |
|---|
| 712 | 683 | .get_stats = bcm7xxx_28nm_get_phy_stats, \ |
|---|
| 713 | 684 | .probe = bcm7xxx_28nm_probe, \ |
|---|
| 685 | + .remove = bcm7xxx_28nm_remove, \ |
|---|
| 714 | 686 | } |
|---|
| 715 | 687 | |
|---|
| 716 | 688 | #define BCM7XXX_28NM_EPHY(_oui, _name) \ |
|---|
| .. | .. |
|---|
| 718 | 690 | .phy_id = (_oui), \ |
|---|
| 719 | 691 | .phy_id_mask = 0xfffffff0, \ |
|---|
| 720 | 692 | .name = _name, \ |
|---|
| 721 | | - .features = PHY_BASIC_FEATURES, \ |
|---|
| 693 | + /* PHY_BASIC_FEATURES */ \ |
|---|
| 722 | 694 | .flags = PHY_IS_INTERNAL, \ |
|---|
| 723 | 695 | .config_init = bcm7xxx_28nm_ephy_config_init, \ |
|---|
| 724 | 696 | .resume = bcm7xxx_28nm_ephy_resume, \ |
|---|
| .. | .. |
|---|
| 726 | 698 | .get_strings = bcm_phy_get_strings, \ |
|---|
| 727 | 699 | .get_stats = bcm7xxx_28nm_get_phy_stats, \ |
|---|
| 728 | 700 | .probe = bcm7xxx_28nm_probe, \ |
|---|
| 701 | + .remove = bcm7xxx_28nm_remove, \ |
|---|
| 729 | 702 | .read_mmd = bcm7xxx_28nm_ephy_read_mmd, \ |
|---|
| 730 | 703 | .write_mmd = bcm7xxx_28nm_ephy_write_mmd, \ |
|---|
| 731 | 704 | } |
|---|
| .. | .. |
|---|
| 735 | 708 | .phy_id = (_oui), \ |
|---|
| 736 | 709 | .phy_id_mask = 0xfffffff0, \ |
|---|
| 737 | 710 | .name = _name, \ |
|---|
| 738 | | - .features = PHY_BASIC_FEATURES, \ |
|---|
| 711 | + /* PHY_BASIC_FEATURES */ \ |
|---|
| 739 | 712 | .flags = PHY_IS_INTERNAL, \ |
|---|
| 740 | 713 | .soft_reset = genphy_soft_reset, \ |
|---|
| 741 | 714 | .config_init = bcm7xxx_config_init, \ |
|---|
| .. | .. |
|---|
| 744 | 717 | } |
|---|
| 745 | 718 | |
|---|
| 746 | 719 | static struct phy_driver bcm7xxx_driver[] = { |
|---|
| 720 | + BCM7XXX_28NM_EPHY(PHY_ID_BCM72113, "Broadcom BCM72113"), |
|---|
| 747 | 721 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), |
|---|
| 722 | + BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255"), |
|---|
| 748 | 723 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"), |
|---|
| 749 | 724 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268"), |
|---|
| 750 | 725 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271"), |
|---|
| .. | .. |
|---|
| 755 | 730 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"), |
|---|
| 756 | 731 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"), |
|---|
| 757 | 732 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"), |
|---|
| 758 | | - BCM7XXX_28NM_GPHY(PHY_ID_BCM_OMEGA, "Broadcom Omega Combo GPHY"), |
|---|
| 759 | 733 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"), |
|---|
| 760 | 734 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"), |
|---|
| 761 | 735 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"), |
|---|
| .. | .. |
|---|
| 764 | 738 | }; |
|---|
| 765 | 739 | |
|---|
| 766 | 740 | static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { |
|---|
| 741 | + { PHY_ID_BCM72113, 0xfffffff0 }, |
|---|
| 767 | 742 | { PHY_ID_BCM7250, 0xfffffff0, }, |
|---|
| 743 | + { PHY_ID_BCM7255, 0xfffffff0, }, |
|---|
| 768 | 744 | { PHY_ID_BCM7260, 0xfffffff0, }, |
|---|
| 769 | 745 | { PHY_ID_BCM7268, 0xfffffff0, }, |
|---|
| 770 | 746 | { PHY_ID_BCM7271, 0xfffffff0, }, |
|---|