Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2015 Broadcom Corporation
0004  */
0005 
0006 /* Broadcom Cygnus SoC internal transceivers support. */
0007 #include "bcm-phy-lib.h"
0008 #include <linux/brcmphy.h>
0009 #include <linux/module.h>
0010 #include <linux/netdevice.h>
0011 #include <linux/phy.h>
0012 
0013 struct bcm_omega_phy_priv {
0014     u64 *stats;
0015 };
0016 
0017 /* Broadcom Cygnus Phy specific registers */
0018 #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0  0x91E5 /* VDAL Control register */
0019 
0020 static int bcm_cygnus_afe_config(struct phy_device *phydev)
0021 {
0022     int rc;
0023 
0024     /* ensure smdspclk is enabled */
0025     rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30);
0026     if (rc < 0)
0027         return rc;
0028 
0029     /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */
0030     rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
0031     if (rc < 0)
0032         return rc;
0033 
0034     /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/
0035     rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
0036     if (rc < 0)
0037         return rc;
0038 
0039     /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */
0040     rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
0041     if (rc < 0)
0042         return rc;
0043 
0044     /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */
0045     rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
0046     if (rc < 0)
0047         return rc;
0048 
0049     /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */
0050     rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
0051     if (rc < 0)
0052         return rc;
0053 
0054     /* Adjust bias current trim to overcome digital offSet */
0055     rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02);
0056     if (rc < 0)
0057         return rc;
0058 
0059     /* make rcal=100, since rdb default is 000 */
0060     rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10);
0061     if (rc < 0)
0062         return rc;
0063 
0064     /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
0065     rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10);
0066     if (rc < 0)
0067         return rc;
0068 
0069     /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
0070     rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00);
0071 
0072     return 0;
0073 }
0074 
0075 static int bcm_cygnus_config_init(struct phy_device *phydev)
0076 {
0077     int reg, rc;
0078 
0079     reg = phy_read(phydev, MII_BCM54XX_ECR);
0080     if (reg < 0)
0081         return reg;
0082 
0083     /* Mask interrupts globally. */
0084     reg |= MII_BCM54XX_ECR_IM;
0085     rc = phy_write(phydev, MII_BCM54XX_ECR, reg);
0086     if (rc)
0087         return rc;
0088 
0089     /* Unmask events of interest */
0090     reg = ~(MII_BCM54XX_INT_DUPLEX |
0091         MII_BCM54XX_INT_SPEED |
0092         MII_BCM54XX_INT_LINK);
0093     rc = phy_write(phydev, MII_BCM54XX_IMR, reg);
0094     if (rc)
0095         return rc;
0096 
0097     /* Apply AFE settings for the PHY */
0098     rc = bcm_cygnus_afe_config(phydev);
0099     if (rc)
0100         return rc;
0101 
0102     /* Advertise EEE */
0103     rc = bcm_phy_set_eee(phydev, true);
0104     if (rc)
0105         return rc;
0106 
0107     /* Enable APD */
0108     return bcm_phy_enable_apd(phydev, false);
0109 }
0110 
0111 static int bcm_cygnus_resume(struct phy_device *phydev)
0112 {
0113     int rc;
0114 
0115     genphy_resume(phydev);
0116 
0117     /* Re-initialize the PHY to apply AFE work-arounds and
0118      * configurations when coming out of suspend.
0119      */
0120     rc = bcm_cygnus_config_init(phydev);
0121     if (rc)
0122         return rc;
0123 
0124     /* restart auto negotiation with the new settings */
0125     return genphy_config_aneg(phydev);
0126 }
0127 
0128 static int bcm_omega_config_init(struct phy_device *phydev)
0129 {
0130     u8 count, rev;
0131     int ret = 0;
0132 
0133     rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
0134 
0135     pr_info_once("%s: %s PHY revision: 0x%02x\n",
0136              phydev_name(phydev), phydev->drv->name, rev);
0137 
0138     /* Dummy read to a register to workaround an issue upon reset where the
0139      * internal inverter may not allow the first MDIO transaction to pass
0140      * the MDIO management controller and make us return 0xffff for such
0141      * reads.
0142      */
0143     phy_read(phydev, MII_BMSR);
0144 
0145     switch (rev) {
0146     case 0x00:
0147         ret = bcm_phy_28nm_a0b0_afe_config_init(phydev);
0148         break;
0149     default:
0150         break;
0151     }
0152 
0153     if (ret)
0154         return ret;
0155 
0156     ret = bcm_phy_downshift_get(phydev, &count);
0157     if (ret)
0158         return ret;
0159 
0160     /* Only enable EEE if Wirespeed/downshift is disabled */
0161     ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
0162     if (ret)
0163         return ret;
0164 
0165     return bcm_phy_enable_apd(phydev, true);
0166 }
0167 
0168 static int bcm_omega_resume(struct phy_device *phydev)
0169 {
0170     int ret;
0171 
0172     /* Re-apply workarounds coming out suspend/resume */
0173     ret = bcm_omega_config_init(phydev);
0174     if (ret)
0175         return ret;
0176 
0177     /* 28nm Gigabit PHYs come out of reset without any half-duplex
0178      * or "hub" compliant advertised mode, fix that. This does not
0179      * cause any problems with the PHY library since genphy_config_aneg()
0180      * gracefully handles auto-negotiated and forced modes.
0181      */
0182     return genphy_config_aneg(phydev);
0183 }
0184 
0185 static int bcm_omega_get_tunable(struct phy_device *phydev,
0186                  struct ethtool_tunable *tuna, void *data)
0187 {
0188     switch (tuna->id) {
0189     case ETHTOOL_PHY_DOWNSHIFT:
0190         return bcm_phy_downshift_get(phydev, (u8 *)data);
0191     default:
0192         return -EOPNOTSUPP;
0193     }
0194 }
0195 
0196 static int bcm_omega_set_tunable(struct phy_device *phydev,
0197                  struct ethtool_tunable *tuna,
0198                  const void *data)
0199 {
0200     u8 count = *(u8 *)data;
0201     int ret;
0202 
0203     switch (tuna->id) {
0204     case ETHTOOL_PHY_DOWNSHIFT:
0205         ret = bcm_phy_downshift_set(phydev, count);
0206         break;
0207     default:
0208         return -EOPNOTSUPP;
0209     }
0210 
0211     if (ret)
0212         return ret;
0213 
0214     /* Disable EEE advertisement since this prevents the PHY
0215      * from successfully linking up, trigger auto-negotiation restart
0216      * to let the MAC decide what to do.
0217      */
0218     ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
0219     if (ret)
0220         return ret;
0221 
0222     return genphy_restart_aneg(phydev);
0223 }
0224 
0225 static void bcm_omega_get_phy_stats(struct phy_device *phydev,
0226                     struct ethtool_stats *stats, u64 *data)
0227 {
0228     struct bcm_omega_phy_priv *priv = phydev->priv;
0229 
0230     bcm_phy_get_stats(phydev, priv->stats, stats, data);
0231 }
0232 
0233 static int bcm_omega_probe(struct phy_device *phydev)
0234 {
0235     struct bcm_omega_phy_priv *priv;
0236 
0237     priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
0238     if (!priv)
0239         return -ENOMEM;
0240 
0241     phydev->priv = priv;
0242 
0243     priv->stats = devm_kcalloc(&phydev->mdio.dev,
0244                    bcm_phy_get_sset_count(phydev), sizeof(u64),
0245                    GFP_KERNEL);
0246     if (!priv->stats)
0247         return -ENOMEM;
0248 
0249     return 0;
0250 }
0251 
0252 static struct phy_driver bcm_cygnus_phy_driver[] = {
0253 {
0254     .phy_id        = PHY_ID_BCM_CYGNUS,
0255     .phy_id_mask   = 0xfffffff0,
0256     .name          = "Broadcom Cygnus PHY",
0257     /* PHY_GBIT_FEATURES */
0258     .config_init   = bcm_cygnus_config_init,
0259     .config_intr   = bcm_phy_config_intr,
0260     .handle_interrupt = bcm_phy_handle_interrupt,
0261     .suspend       = genphy_suspend,
0262     .resume        = bcm_cygnus_resume,
0263 }, {
0264     .phy_id     = PHY_ID_BCM_OMEGA,
0265     .phy_id_mask    = 0xfffffff0,
0266     .name       = "Broadcom Omega Combo GPHY",
0267     /* PHY_GBIT_FEATURES */
0268     .flags      = PHY_IS_INTERNAL,
0269     .config_init    = bcm_omega_config_init,
0270     .suspend    = genphy_suspend,
0271     .resume     = bcm_omega_resume,
0272     .get_tunable    = bcm_omega_get_tunable,
0273     .set_tunable    = bcm_omega_set_tunable,
0274     .get_sset_count = bcm_phy_get_sset_count,
0275     .get_strings    = bcm_phy_get_strings,
0276     .get_stats  = bcm_omega_get_phy_stats,
0277     .probe      = bcm_omega_probe,
0278 }
0279 };
0280 
0281 static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = {
0282     { PHY_ID_BCM_CYGNUS, 0xfffffff0, },
0283     { PHY_ID_BCM_OMEGA, 0xfffffff0, },
0284     { }
0285 };
0286 MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl);
0287 
0288 module_phy_driver(bcm_cygnus_phy_driver);
0289 
0290 MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver");
0291 MODULE_LICENSE("GPL v2");
0292 MODULE_AUTHOR("Broadcom Corporation");