0001
0002
0003
0004
0005
0006
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
0018 #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0 0x91E5
0019
0020 static int bcm_cygnus_afe_config(struct phy_device *phydev)
0021 {
0022 int rc;
0023
0024
0025 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30);
0026 if (rc < 0)
0027 return rc;
0028
0029
0030 rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
0031 if (rc < 0)
0032 return rc;
0033
0034
0035 rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
0036 if (rc < 0)
0037 return rc;
0038
0039
0040 rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
0041 if (rc < 0)
0042 return rc;
0043
0044
0045 rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
0046 if (rc < 0)
0047 return rc;
0048
0049
0050 rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
0051 if (rc < 0)
0052 return rc;
0053
0054
0055 rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02);
0056 if (rc < 0)
0057 return rc;
0058
0059
0060 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10);
0061 if (rc < 0)
0062 return rc;
0063
0064
0065 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10);
0066 if (rc < 0)
0067 return rc;
0068
0069
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
0084 reg |= MII_BCM54XX_ECR_IM;
0085 rc = phy_write(phydev, MII_BCM54XX_ECR, reg);
0086 if (rc)
0087 return rc;
0088
0089
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
0098 rc = bcm_cygnus_afe_config(phydev);
0099 if (rc)
0100 return rc;
0101
0102
0103 rc = bcm_phy_set_eee(phydev, true);
0104 if (rc)
0105 return rc;
0106
0107
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
0118
0119
0120 rc = bcm_cygnus_config_init(phydev);
0121 if (rc)
0122 return rc;
0123
0124
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
0139
0140
0141
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
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
0173 ret = bcm_omega_config_init(phydev);
0174 if (ret)
0175 return ret;
0176
0177
0178
0179
0180
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
0215
0216
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
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
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");