0001
0002
0003
0004
0005
0006
0007 #include <linux/kernel.h>
0008 #include <linux/capability.h>
0009 #include <linux/errno.h>
0010 #include <linux/ethtool.h>
0011 #include <linux/mdio.h>
0012 #include <linux/module.h>
0013
0014 MODULE_DESCRIPTION("Generic support for MDIO-compatible transceivers");
0015 MODULE_AUTHOR("Copyright 2006-2009 Solarflare Communications Inc.");
0016 MODULE_LICENSE("GPL");
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026 int mdio45_probe(struct mdio_if_info *mdio, int prtad)
0027 {
0028 int mmd, stat2, devs1, devs2;
0029
0030
0031
0032 for (mmd = 1; mmd <= 5; mmd++) {
0033
0034 stat2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_STAT2);
0035 if (stat2 < 0 ||
0036 (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL)
0037 continue;
0038
0039
0040 devs1 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS1);
0041 devs2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS2);
0042 if (devs1 < 0 || devs2 < 0)
0043 continue;
0044
0045 mdio->prtad = prtad;
0046 mdio->mmds = devs1 | (devs2 << 16);
0047 return 0;
0048 }
0049
0050 return -ENODEV;
0051 }
0052 EXPORT_SYMBOL(mdio45_probe);
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066 int mdio_set_flag(const struct mdio_if_info *mdio,
0067 int prtad, int devad, u16 addr, int mask,
0068 bool sense)
0069 {
0070 int old_val = mdio->mdio_read(mdio->dev, prtad, devad, addr);
0071 int new_val;
0072
0073 if (old_val < 0)
0074 return old_val;
0075 if (sense)
0076 new_val = old_val | mask;
0077 else
0078 new_val = old_val & ~mask;
0079 if (old_val == new_val)
0080 return 0;
0081 return mdio->mdio_write(mdio->dev, prtad, devad, addr, new_val);
0082 }
0083 EXPORT_SYMBOL(mdio_set_flag);
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094 int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmd_mask)
0095 {
0096 int devad, reg;
0097
0098 if (!mmd_mask) {
0099
0100 reg = mdio->mdio_read(mdio->dev, mdio->prtad,
0101 MDIO_MMD_PHYXS, MDIO_STAT2);
0102 return reg >= 0 && !(reg & MDIO_STAT2_RXFAULT);
0103 }
0104
0105 for (devad = 0; mmd_mask; devad++) {
0106 if (mmd_mask & (1 << devad)) {
0107 mmd_mask &= ~(1 << devad);
0108
0109
0110 mdio->mdio_read(mdio->dev, mdio->prtad,
0111 devad, MDIO_STAT1);
0112 if (devad == MDIO_MMD_PMAPMD || devad == MDIO_MMD_PCS ||
0113 devad == MDIO_MMD_PHYXS || devad == MDIO_MMD_DTEXS)
0114 mdio->mdio_read(mdio->dev, mdio->prtad,
0115 devad, MDIO_STAT2);
0116
0117
0118 reg = mdio->mdio_read(mdio->dev, mdio->prtad,
0119 devad, MDIO_STAT1);
0120 if (reg < 0 ||
0121 (reg & (MDIO_STAT1_FAULT | MDIO_STAT1_LSTATUS)) !=
0122 MDIO_STAT1_LSTATUS)
0123 return false;
0124 }
0125 }
0126
0127 return true;
0128 }
0129 EXPORT_SYMBOL(mdio45_links_ok);
0130
0131
0132
0133
0134
0135
0136
0137 int mdio45_nway_restart(const struct mdio_if_info *mdio)
0138 {
0139 if (!(mdio->mmds & MDIO_DEVS_AN))
0140 return -EOPNOTSUPP;
0141
0142 mdio_set_flag(mdio, mdio->prtad, MDIO_MMD_AN, MDIO_CTRL1,
0143 MDIO_AN_CTRL1_RESTART, true);
0144 return 0;
0145 }
0146 EXPORT_SYMBOL(mdio45_nway_restart);
0147
0148 static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr)
0149 {
0150 u32 result = 0;
0151 int reg;
0152
0153 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, addr);
0154 if (reg & ADVERTISE_10HALF)
0155 result |= ADVERTISED_10baseT_Half;
0156 if (reg & ADVERTISE_10FULL)
0157 result |= ADVERTISED_10baseT_Full;
0158 if (reg & ADVERTISE_100HALF)
0159 result |= ADVERTISED_100baseT_Half;
0160 if (reg & ADVERTISE_100FULL)
0161 result |= ADVERTISED_100baseT_Full;
0162 if (reg & ADVERTISE_PAUSE_CAP)
0163 result |= ADVERTISED_Pause;
0164 if (reg & ADVERTISE_PAUSE_ASYM)
0165 result |= ADVERTISED_Asym_Pause;
0166 return result;
0167 }
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183 void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio,
0184 struct ethtool_cmd *ecmd,
0185 u32 npage_adv, u32 npage_lpa)
0186 {
0187 int reg;
0188 u32 speed;
0189
0190 BUILD_BUG_ON(MDIO_SUPPORTS_C22 != ETH_MDIO_SUPPORTS_C22);
0191 BUILD_BUG_ON(MDIO_SUPPORTS_C45 != ETH_MDIO_SUPPORTS_C45);
0192
0193 ecmd->transceiver = XCVR_INTERNAL;
0194 ecmd->phy_address = mdio->prtad;
0195 ecmd->mdio_support =
0196 mdio->mode_support & (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22);
0197
0198 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0199 MDIO_CTRL2);
0200 switch (reg & MDIO_PMA_CTRL2_TYPE) {
0201 case MDIO_PMA_CTRL2_10GBT:
0202 case MDIO_PMA_CTRL2_1000BT:
0203 case MDIO_PMA_CTRL2_100BTX:
0204 case MDIO_PMA_CTRL2_10BT:
0205 ecmd->port = PORT_TP;
0206 ecmd->supported = SUPPORTED_TP;
0207 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0208 MDIO_SPEED);
0209 if (reg & MDIO_SPEED_10G)
0210 ecmd->supported |= SUPPORTED_10000baseT_Full;
0211 if (reg & MDIO_PMA_SPEED_1000)
0212 ecmd->supported |= (SUPPORTED_1000baseT_Full |
0213 SUPPORTED_1000baseT_Half);
0214 if (reg & MDIO_PMA_SPEED_100)
0215 ecmd->supported |= (SUPPORTED_100baseT_Full |
0216 SUPPORTED_100baseT_Half);
0217 if (reg & MDIO_PMA_SPEED_10)
0218 ecmd->supported |= (SUPPORTED_10baseT_Full |
0219 SUPPORTED_10baseT_Half);
0220 ecmd->advertising = ADVERTISED_TP;
0221 break;
0222
0223 case MDIO_PMA_CTRL2_10GBCX4:
0224 ecmd->port = PORT_OTHER;
0225 ecmd->supported = 0;
0226 ecmd->advertising = 0;
0227 break;
0228
0229 case MDIO_PMA_CTRL2_10GBKX4:
0230 case MDIO_PMA_CTRL2_10GBKR:
0231 case MDIO_PMA_CTRL2_1000BKX:
0232 ecmd->port = PORT_OTHER;
0233 ecmd->supported = SUPPORTED_Backplane;
0234 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0235 MDIO_PMA_EXTABLE);
0236 if (reg & MDIO_PMA_EXTABLE_10GBKX4)
0237 ecmd->supported |= SUPPORTED_10000baseKX4_Full;
0238 if (reg & MDIO_PMA_EXTABLE_10GBKR)
0239 ecmd->supported |= SUPPORTED_10000baseKR_Full;
0240 if (reg & MDIO_PMA_EXTABLE_1000BKX)
0241 ecmd->supported |= SUPPORTED_1000baseKX_Full;
0242 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0243 MDIO_PMA_10GBR_FECABLE);
0244 if (reg & MDIO_PMA_10GBR_FECABLE_ABLE)
0245 ecmd->supported |= SUPPORTED_10000baseR_FEC;
0246 ecmd->advertising = ADVERTISED_Backplane;
0247 break;
0248
0249
0250 default:
0251 ecmd->port = PORT_FIBRE;
0252 ecmd->supported = SUPPORTED_FIBRE;
0253 ecmd->advertising = ADVERTISED_FIBRE;
0254 break;
0255 }
0256
0257 if (mdio->mmds & MDIO_DEVS_AN) {
0258 ecmd->supported |= SUPPORTED_Autoneg;
0259 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
0260 MDIO_CTRL1);
0261 if (reg & MDIO_AN_CTRL1_ENABLE) {
0262 ecmd->autoneg = AUTONEG_ENABLE;
0263 ecmd->advertising |=
0264 ADVERTISED_Autoneg |
0265 mdio45_get_an(mdio, MDIO_AN_ADVERTISE) |
0266 npage_adv;
0267 } else {
0268 ecmd->autoneg = AUTONEG_DISABLE;
0269 }
0270 } else {
0271 ecmd->autoneg = AUTONEG_DISABLE;
0272 }
0273
0274 if (ecmd->autoneg) {
0275 u32 modes = 0;
0276 int an_stat = mdio->mdio_read(mdio->dev, mdio->prtad,
0277 MDIO_MMD_AN, MDIO_STAT1);
0278
0279
0280
0281 if (an_stat & MDIO_AN_STAT1_COMPLETE) {
0282 ecmd->lp_advertising =
0283 mdio45_get_an(mdio, MDIO_AN_LPA) | npage_lpa;
0284 if (an_stat & MDIO_AN_STAT1_LPABLE)
0285 ecmd->lp_advertising |= ADVERTISED_Autoneg;
0286 modes = ecmd->advertising & ecmd->lp_advertising;
0287 }
0288 if ((modes & ~ADVERTISED_Autoneg) == 0)
0289 modes = ecmd->advertising;
0290
0291 if (modes & (ADVERTISED_10000baseT_Full |
0292 ADVERTISED_10000baseKX4_Full |
0293 ADVERTISED_10000baseKR_Full)) {
0294 speed = SPEED_10000;
0295 ecmd->duplex = DUPLEX_FULL;
0296 } else if (modes & (ADVERTISED_1000baseT_Full |
0297 ADVERTISED_1000baseT_Half |
0298 ADVERTISED_1000baseKX_Full)) {
0299 speed = SPEED_1000;
0300 ecmd->duplex = !(modes & ADVERTISED_1000baseT_Half);
0301 } else if (modes & (ADVERTISED_100baseT_Full |
0302 ADVERTISED_100baseT_Half)) {
0303 speed = SPEED_100;
0304 ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full);
0305 } else {
0306 speed = SPEED_10;
0307 ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full);
0308 }
0309 } else {
0310
0311 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0312 MDIO_CTRL1);
0313 speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1)
0314 * ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10));
0315 ecmd->duplex = (reg & MDIO_CTRL1_FULLDPLX ||
0316 speed == SPEED_10000);
0317 }
0318
0319 ethtool_cmd_speed_set(ecmd, speed);
0320
0321
0322 if (ecmd->port == PORT_TP
0323 && (ethtool_cmd_speed(ecmd) == SPEED_10000)) {
0324 switch (mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0325 MDIO_PMA_10GBT_SWAPPOL)) {
0326 case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX:
0327 ecmd->eth_tp_mdix = ETH_TP_MDI;
0328 break;
0329 case 0:
0330 ecmd->eth_tp_mdix = ETH_TP_MDI_X;
0331 break;
0332 default:
0333
0334 ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
0335 break;
0336 }
0337 }
0338 }
0339 EXPORT_SYMBOL(mdio45_ethtool_gset_npage);
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355 void mdio45_ethtool_ksettings_get_npage(const struct mdio_if_info *mdio,
0356 struct ethtool_link_ksettings *cmd,
0357 u32 npage_adv, u32 npage_lpa)
0358 {
0359 int reg;
0360 u32 speed, supported = 0, advertising = 0, lp_advertising = 0;
0361
0362 BUILD_BUG_ON(MDIO_SUPPORTS_C22 != ETH_MDIO_SUPPORTS_C22);
0363 BUILD_BUG_ON(MDIO_SUPPORTS_C45 != ETH_MDIO_SUPPORTS_C45);
0364
0365 cmd->base.phy_address = mdio->prtad;
0366 cmd->base.mdio_support =
0367 mdio->mode_support & (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22);
0368
0369 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0370 MDIO_CTRL2);
0371 switch (reg & MDIO_PMA_CTRL2_TYPE) {
0372 case MDIO_PMA_CTRL2_10GBT:
0373 case MDIO_PMA_CTRL2_1000BT:
0374 case MDIO_PMA_CTRL2_100BTX:
0375 case MDIO_PMA_CTRL2_10BT:
0376 cmd->base.port = PORT_TP;
0377 supported = SUPPORTED_TP;
0378 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0379 MDIO_SPEED);
0380 if (reg & MDIO_SPEED_10G)
0381 supported |= SUPPORTED_10000baseT_Full;
0382 if (reg & MDIO_PMA_SPEED_1000)
0383 supported |= (SUPPORTED_1000baseT_Full |
0384 SUPPORTED_1000baseT_Half);
0385 if (reg & MDIO_PMA_SPEED_100)
0386 supported |= (SUPPORTED_100baseT_Full |
0387 SUPPORTED_100baseT_Half);
0388 if (reg & MDIO_PMA_SPEED_10)
0389 supported |= (SUPPORTED_10baseT_Full |
0390 SUPPORTED_10baseT_Half);
0391 advertising = ADVERTISED_TP;
0392 break;
0393
0394 case MDIO_PMA_CTRL2_10GBCX4:
0395 cmd->base.port = PORT_OTHER;
0396 supported = 0;
0397 advertising = 0;
0398 break;
0399
0400 case MDIO_PMA_CTRL2_10GBKX4:
0401 case MDIO_PMA_CTRL2_10GBKR:
0402 case MDIO_PMA_CTRL2_1000BKX:
0403 cmd->base.port = PORT_OTHER;
0404 supported = SUPPORTED_Backplane;
0405 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0406 MDIO_PMA_EXTABLE);
0407 if (reg & MDIO_PMA_EXTABLE_10GBKX4)
0408 supported |= SUPPORTED_10000baseKX4_Full;
0409 if (reg & MDIO_PMA_EXTABLE_10GBKR)
0410 supported |= SUPPORTED_10000baseKR_Full;
0411 if (reg & MDIO_PMA_EXTABLE_1000BKX)
0412 supported |= SUPPORTED_1000baseKX_Full;
0413 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0414 MDIO_PMA_10GBR_FECABLE);
0415 if (reg & MDIO_PMA_10GBR_FECABLE_ABLE)
0416 supported |= SUPPORTED_10000baseR_FEC;
0417 advertising = ADVERTISED_Backplane;
0418 break;
0419
0420
0421 default:
0422 cmd->base.port = PORT_FIBRE;
0423 supported = SUPPORTED_FIBRE;
0424 advertising = ADVERTISED_FIBRE;
0425 break;
0426 }
0427
0428 if (mdio->mmds & MDIO_DEVS_AN) {
0429 supported |= SUPPORTED_Autoneg;
0430 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
0431 MDIO_CTRL1);
0432 if (reg & MDIO_AN_CTRL1_ENABLE) {
0433 cmd->base.autoneg = AUTONEG_ENABLE;
0434 advertising |=
0435 ADVERTISED_Autoneg |
0436 mdio45_get_an(mdio, MDIO_AN_ADVERTISE) |
0437 npage_adv;
0438 } else {
0439 cmd->base.autoneg = AUTONEG_DISABLE;
0440 }
0441 } else {
0442 cmd->base.autoneg = AUTONEG_DISABLE;
0443 }
0444
0445 if (cmd->base.autoneg) {
0446 u32 modes = 0;
0447 int an_stat = mdio->mdio_read(mdio->dev, mdio->prtad,
0448 MDIO_MMD_AN, MDIO_STAT1);
0449
0450
0451
0452
0453 if (an_stat & MDIO_AN_STAT1_COMPLETE) {
0454 lp_advertising =
0455 mdio45_get_an(mdio, MDIO_AN_LPA) | npage_lpa;
0456 if (an_stat & MDIO_AN_STAT1_LPABLE)
0457 lp_advertising |= ADVERTISED_Autoneg;
0458 modes = advertising & lp_advertising;
0459 }
0460 if ((modes & ~ADVERTISED_Autoneg) == 0)
0461 modes = advertising;
0462
0463 if (modes & (ADVERTISED_10000baseT_Full |
0464 ADVERTISED_10000baseKX4_Full |
0465 ADVERTISED_10000baseKR_Full)) {
0466 speed = SPEED_10000;
0467 cmd->base.duplex = DUPLEX_FULL;
0468 } else if (modes & (ADVERTISED_1000baseT_Full |
0469 ADVERTISED_1000baseT_Half |
0470 ADVERTISED_1000baseKX_Full)) {
0471 speed = SPEED_1000;
0472 cmd->base.duplex = !(modes & ADVERTISED_1000baseT_Half);
0473 } else if (modes & (ADVERTISED_100baseT_Full |
0474 ADVERTISED_100baseT_Half)) {
0475 speed = SPEED_100;
0476 cmd->base.duplex = !!(modes & ADVERTISED_100baseT_Full);
0477 } else {
0478 speed = SPEED_10;
0479 cmd->base.duplex = !!(modes & ADVERTISED_10baseT_Full);
0480 }
0481 } else {
0482
0483 reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0484 MDIO_CTRL1);
0485 speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1)
0486 * ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10));
0487 cmd->base.duplex = (reg & MDIO_CTRL1_FULLDPLX ||
0488 speed == SPEED_10000);
0489 }
0490
0491 cmd->base.speed = speed;
0492
0493 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
0494 supported);
0495 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
0496 advertising);
0497 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
0498 lp_advertising);
0499
0500
0501 if (cmd->base.port == PORT_TP && (cmd->base.speed == SPEED_10000)) {
0502 switch (mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
0503 MDIO_PMA_10GBT_SWAPPOL)) {
0504 case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX:
0505 cmd->base.eth_tp_mdix = ETH_TP_MDI;
0506 break;
0507 case 0:
0508 cmd->base.eth_tp_mdix = ETH_TP_MDI_X;
0509 break;
0510 default:
0511
0512 cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
0513 break;
0514 }
0515 }
0516 }
0517 EXPORT_SYMBOL(mdio45_ethtool_ksettings_get_npage);
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527 int mdio_mii_ioctl(const struct mdio_if_info *mdio,
0528 struct mii_ioctl_data *mii_data, int cmd)
0529 {
0530 int prtad, devad;
0531 u16 addr = mii_data->reg_num;
0532
0533
0534 switch (cmd) {
0535 case SIOCGMIIPHY:
0536 if (mdio->prtad == MDIO_PRTAD_NONE)
0537 return -EOPNOTSUPP;
0538 mii_data->phy_id = mdio->prtad;
0539 cmd = SIOCGMIIREG;
0540 break;
0541 case SIOCGMIIREG:
0542 case SIOCSMIIREG:
0543 break;
0544 default:
0545 return -EOPNOTSUPP;
0546 }
0547
0548
0549 if ((mdio->mode_support & MDIO_SUPPORTS_C45) &&
0550 mdio_phy_id_is_c45(mii_data->phy_id)) {
0551 prtad = mdio_phy_id_prtad(mii_data->phy_id);
0552 devad = mdio_phy_id_devad(mii_data->phy_id);
0553 } else if ((mdio->mode_support & MDIO_SUPPORTS_C22) &&
0554 mii_data->phy_id < 0x20) {
0555 prtad = mii_data->phy_id;
0556 devad = MDIO_DEVAD_NONE;
0557 addr &= 0x1f;
0558 } else if ((mdio->mode_support & MDIO_EMULATE_C22) &&
0559 mdio->prtad != MDIO_PRTAD_NONE &&
0560 mii_data->phy_id == mdio->prtad) {
0561
0562 prtad = mdio->prtad;
0563 switch (addr) {
0564 case MII_BMCR:
0565 case MII_BMSR:
0566 case MII_PHYSID1:
0567 case MII_PHYSID2:
0568 devad = __ffs(mdio->mmds);
0569 break;
0570 case MII_ADVERTISE:
0571 case MII_LPA:
0572 if (!(mdio->mmds & MDIO_DEVS_AN))
0573 return -EINVAL;
0574 devad = MDIO_MMD_AN;
0575 if (addr == MII_ADVERTISE)
0576 addr = MDIO_AN_ADVERTISE;
0577 else
0578 addr = MDIO_AN_LPA;
0579 break;
0580 default:
0581 return -EINVAL;
0582 }
0583 } else {
0584 return -EINVAL;
0585 }
0586
0587 if (cmd == SIOCGMIIREG) {
0588 int rc = mdio->mdio_read(mdio->dev, prtad, devad, addr);
0589 if (rc < 0)
0590 return rc;
0591 mii_data->val_out = rc;
0592 return 0;
0593 } else {
0594 return mdio->mdio_write(mdio->dev, prtad, devad, addr,
0595 mii_data->val_in);
0596 }
0597 }
0598 EXPORT_SYMBOL(mdio_mii_ioctl);