0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030 #include <linux/kernel.h>
0031 #include <linux/module.h>
0032 #include <linux/netdevice.h>
0033 #include <linux/ethtool.h>
0034 #include <linux/mii.h>
0035
0036 static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
0037 {
0038 int advert;
0039
0040 advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
0041
0042 return mii_lpa_to_ethtool_lpa_t(advert);
0043 }
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053 void mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
0054 {
0055 struct net_device *dev = mii->dev;
0056 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
0057 u32 nego;
0058
0059 ecmd->supported =
0060 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
0061 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
0062 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
0063 if (mii->supports_gmii)
0064 ecmd->supported |= SUPPORTED_1000baseT_Half |
0065 SUPPORTED_1000baseT_Full;
0066
0067
0068 ecmd->port = PORT_MII;
0069
0070
0071 ecmd->transceiver = XCVR_INTERNAL;
0072
0073
0074 ecmd->phy_address = mii->phy_id;
0075 ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
0076
0077 ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
0078
0079 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0080 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
0081 if (mii->supports_gmii) {
0082 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
0083 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
0084 }
0085
0086 ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
0087 if (mii->supports_gmii)
0088 ecmd->advertising |=
0089 mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
0090
0091 if (bmcr & BMCR_ANENABLE) {
0092 ecmd->advertising |= ADVERTISED_Autoneg;
0093 ecmd->autoneg = AUTONEG_ENABLE;
0094
0095 if (bmsr & BMSR_ANEGCOMPLETE) {
0096 ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
0097 ecmd->lp_advertising |=
0098 mii_stat1000_to_ethtool_lpa_t(stat1000);
0099 } else {
0100 ecmd->lp_advertising = 0;
0101 }
0102
0103 nego = ecmd->advertising & ecmd->lp_advertising;
0104
0105 if (nego & (ADVERTISED_1000baseT_Full |
0106 ADVERTISED_1000baseT_Half)) {
0107 ethtool_cmd_speed_set(ecmd, SPEED_1000);
0108 ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
0109 } else if (nego & (ADVERTISED_100baseT_Full |
0110 ADVERTISED_100baseT_Half)) {
0111 ethtool_cmd_speed_set(ecmd, SPEED_100);
0112 ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
0113 } else {
0114 ethtool_cmd_speed_set(ecmd, SPEED_10);
0115 ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
0116 }
0117 } else {
0118 ecmd->autoneg = AUTONEG_DISABLE;
0119
0120 ethtool_cmd_speed_set(ecmd,
0121 ((bmcr & BMCR_SPEED1000 &&
0122 (bmcr & BMCR_SPEED100) == 0) ?
0123 SPEED_1000 :
0124 ((bmcr & BMCR_SPEED100) ?
0125 SPEED_100 : SPEED_10)));
0126 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
0127 }
0128
0129 mii->full_duplex = ecmd->duplex;
0130
0131
0132 }
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142 void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
0143 struct ethtool_link_ksettings *cmd)
0144 {
0145 struct net_device *dev = mii->dev;
0146 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
0147 u32 nego, supported, advertising, lp_advertising;
0148
0149 supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
0150 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
0151 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
0152 if (mii->supports_gmii)
0153 supported |= SUPPORTED_1000baseT_Half |
0154 SUPPORTED_1000baseT_Full;
0155
0156
0157 cmd->base.port = PORT_MII;
0158
0159
0160 cmd->base.phy_address = mii->phy_id;
0161 cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
0162
0163 advertising = ADVERTISED_TP | ADVERTISED_MII;
0164
0165 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0166 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
0167 if (mii->supports_gmii) {
0168 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
0169 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
0170 }
0171
0172 advertising |= mii_get_an(mii, MII_ADVERTISE);
0173 if (mii->supports_gmii)
0174 advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
0175
0176 if (bmcr & BMCR_ANENABLE) {
0177 advertising |= ADVERTISED_Autoneg;
0178 cmd->base.autoneg = AUTONEG_ENABLE;
0179
0180 if (bmsr & BMSR_ANEGCOMPLETE) {
0181 lp_advertising = mii_get_an(mii, MII_LPA);
0182 lp_advertising |=
0183 mii_stat1000_to_ethtool_lpa_t(stat1000);
0184 } else {
0185 lp_advertising = 0;
0186 }
0187
0188 nego = advertising & lp_advertising;
0189
0190 if (nego & (ADVERTISED_1000baseT_Full |
0191 ADVERTISED_1000baseT_Half)) {
0192 cmd->base.speed = SPEED_1000;
0193 cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
0194 } else if (nego & (ADVERTISED_100baseT_Full |
0195 ADVERTISED_100baseT_Half)) {
0196 cmd->base.speed = SPEED_100;
0197 cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
0198 } else {
0199 cmd->base.speed = SPEED_10;
0200 cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
0201 }
0202 } else {
0203 cmd->base.autoneg = AUTONEG_DISABLE;
0204
0205 cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
0206 (bmcr & BMCR_SPEED100) == 0) ?
0207 SPEED_1000 :
0208 ((bmcr & BMCR_SPEED100) ?
0209 SPEED_100 : SPEED_10));
0210 cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
0211 DUPLEX_FULL : DUPLEX_HALF;
0212
0213 lp_advertising = 0;
0214 }
0215
0216 mii->full_duplex = cmd->base.duplex;
0217
0218 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
0219 supported);
0220 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
0221 advertising);
0222 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
0223 lp_advertising);
0224
0225
0226 }
0227
0228
0229
0230
0231
0232
0233
0234
0235 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
0236 {
0237 struct net_device *dev = mii->dev;
0238 u32 speed = ethtool_cmd_speed(ecmd);
0239
0240 if (speed != SPEED_10 &&
0241 speed != SPEED_100 &&
0242 speed != SPEED_1000)
0243 return -EINVAL;
0244 if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
0245 return -EINVAL;
0246 if (ecmd->port != PORT_MII)
0247 return -EINVAL;
0248 if (ecmd->transceiver != XCVR_INTERNAL)
0249 return -EINVAL;
0250 if (ecmd->phy_address != mii->phy_id)
0251 return -EINVAL;
0252 if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
0253 return -EINVAL;
0254 if ((speed == SPEED_1000) && (!mii->supports_gmii))
0255 return -EINVAL;
0256
0257
0258
0259 if (ecmd->autoneg == AUTONEG_ENABLE) {
0260 u32 bmcr, advert, tmp;
0261 u32 advert2 = 0, tmp2 = 0;
0262
0263 if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
0264 ADVERTISED_10baseT_Full |
0265 ADVERTISED_100baseT_Half |
0266 ADVERTISED_100baseT_Full |
0267 ADVERTISED_1000baseT_Half |
0268 ADVERTISED_1000baseT_Full)) == 0)
0269 return -EINVAL;
0270
0271
0272 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
0273 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
0274 if (mii->supports_gmii) {
0275 advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
0276 tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
0277 }
0278 tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
0279
0280 if (mii->supports_gmii)
0281 tmp2 |=
0282 ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
0283 if (advert != tmp) {
0284 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
0285 mii->advertising = tmp;
0286 }
0287 if ((mii->supports_gmii) && (advert2 != tmp2))
0288 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
0289
0290
0291 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0292 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
0293 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
0294
0295 mii->force_media = 0;
0296 } else {
0297 u32 bmcr, tmp;
0298
0299
0300 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0301 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
0302 BMCR_SPEED1000 | BMCR_FULLDPLX);
0303 if (speed == SPEED_1000)
0304 tmp |= BMCR_SPEED1000;
0305 else if (speed == SPEED_100)
0306 tmp |= BMCR_SPEED100;
0307 if (ecmd->duplex == DUPLEX_FULL) {
0308 tmp |= BMCR_FULLDPLX;
0309 mii->full_duplex = 1;
0310 } else
0311 mii->full_duplex = 0;
0312 if (bmcr != tmp)
0313 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
0314
0315 mii->force_media = 1;
0316 }
0317 return 0;
0318 }
0319
0320
0321
0322
0323
0324
0325
0326
0327 int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
0328 const struct ethtool_link_ksettings *cmd)
0329 {
0330 struct net_device *dev = mii->dev;
0331 u32 speed = cmd->base.speed;
0332
0333 if (speed != SPEED_10 &&
0334 speed != SPEED_100 &&
0335 speed != SPEED_1000)
0336 return -EINVAL;
0337 if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
0338 return -EINVAL;
0339 if (cmd->base.port != PORT_MII)
0340 return -EINVAL;
0341 if (cmd->base.phy_address != mii->phy_id)
0342 return -EINVAL;
0343 if (cmd->base.autoneg != AUTONEG_DISABLE &&
0344 cmd->base.autoneg != AUTONEG_ENABLE)
0345 return -EINVAL;
0346 if ((speed == SPEED_1000) && (!mii->supports_gmii))
0347 return -EINVAL;
0348
0349
0350
0351 if (cmd->base.autoneg == AUTONEG_ENABLE) {
0352 u32 bmcr, advert, tmp;
0353 u32 advert2 = 0, tmp2 = 0;
0354 u32 advertising;
0355
0356 ethtool_convert_link_mode_to_legacy_u32(
0357 &advertising, cmd->link_modes.advertising);
0358
0359 if ((advertising & (ADVERTISED_10baseT_Half |
0360 ADVERTISED_10baseT_Full |
0361 ADVERTISED_100baseT_Half |
0362 ADVERTISED_100baseT_Full |
0363 ADVERTISED_1000baseT_Half |
0364 ADVERTISED_1000baseT_Full)) == 0)
0365 return -EINVAL;
0366
0367
0368 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
0369 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
0370 if (mii->supports_gmii) {
0371 advert2 = mii->mdio_read(dev, mii->phy_id,
0372 MII_CTRL1000);
0373 tmp2 = advert2 &
0374 ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
0375 }
0376 tmp |= ethtool_adv_to_mii_adv_t(advertising);
0377
0378 if (mii->supports_gmii)
0379 tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
0380 if (advert != tmp) {
0381 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
0382 mii->advertising = tmp;
0383 }
0384 if ((mii->supports_gmii) && (advert2 != tmp2))
0385 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
0386
0387
0388 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0389 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
0390 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
0391
0392 mii->force_media = 0;
0393 } else {
0394 u32 bmcr, tmp;
0395
0396
0397 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0398 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
0399 BMCR_SPEED1000 | BMCR_FULLDPLX);
0400 if (speed == SPEED_1000)
0401 tmp |= BMCR_SPEED1000;
0402 else if (speed == SPEED_100)
0403 tmp |= BMCR_SPEED100;
0404 if (cmd->base.duplex == DUPLEX_FULL) {
0405 tmp |= BMCR_FULLDPLX;
0406 mii->full_duplex = 1;
0407 } else {
0408 mii->full_duplex = 0;
0409 }
0410 if (bmcr != tmp)
0411 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
0412
0413 mii->force_media = 1;
0414 }
0415 return 0;
0416 }
0417
0418
0419
0420
0421
0422 int mii_check_gmii_support(struct mii_if_info *mii)
0423 {
0424 int reg;
0425
0426 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
0427 if (reg & BMSR_ESTATEN) {
0428 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
0429 if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
0430 return 1;
0431 }
0432
0433 return 0;
0434 }
0435
0436
0437
0438
0439
0440
0441
0442 int mii_link_ok (struct mii_if_info *mii)
0443 {
0444
0445 mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
0446 if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
0447 return 1;
0448 return 0;
0449 }
0450
0451
0452
0453
0454
0455
0456
0457 int mii_nway_restart (struct mii_if_info *mii)
0458 {
0459 int bmcr;
0460 int r = -EINVAL;
0461
0462
0463 bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
0464
0465 if (bmcr & BMCR_ANENABLE) {
0466 bmcr |= BMCR_ANRESTART;
0467 mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
0468 r = 0;
0469 }
0470
0471 return r;
0472 }
0473
0474
0475
0476
0477
0478
0479
0480
0481
0482 void mii_check_link (struct mii_if_info *mii)
0483 {
0484 int cur_link = mii_link_ok(mii);
0485 int prev_link = netif_carrier_ok(mii->dev);
0486
0487 if (cur_link && !prev_link)
0488 netif_carrier_on(mii->dev);
0489 else if (prev_link && !cur_link)
0490 netif_carrier_off(mii->dev);
0491 }
0492
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502 unsigned int mii_check_media (struct mii_if_info *mii,
0503 unsigned int ok_to_print,
0504 unsigned int init_media)
0505 {
0506 unsigned int old_carrier, new_carrier;
0507 int advertise, lpa, media, duplex;
0508 int lpa2 = 0;
0509
0510
0511 old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
0512 new_carrier = (unsigned int) mii_link_ok(mii);
0513
0514
0515
0516
0517 if ((!init_media) && (old_carrier == new_carrier))
0518 return 0;
0519
0520
0521 if (!new_carrier) {
0522 netif_carrier_off(mii->dev);
0523 if (ok_to_print)
0524 netdev_info(mii->dev, "link down\n");
0525 return 0;
0526 }
0527
0528
0529
0530
0531 netif_carrier_on(mii->dev);
0532
0533 if (mii->force_media) {
0534 if (ok_to_print)
0535 netdev_info(mii->dev, "link up\n");
0536 return 0;
0537 }
0538
0539
0540 if ((!init_media) && (mii->advertising))
0541 advertise = mii->advertising;
0542 else {
0543 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
0544 mii->advertising = advertise;
0545 }
0546 lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
0547 if (mii->supports_gmii)
0548 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
0549
0550
0551 media = mii_nway_result(lpa & advertise);
0552 duplex = (media & ADVERTISE_FULL) ? 1 : 0;
0553 if (lpa2 & LPA_1000FULL)
0554 duplex = 1;
0555
0556 if (ok_to_print)
0557 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
0558 lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
0559 media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
0560 100 : 10,
0561 duplex ? "full" : "half",
0562 lpa);
0563
0564 if ((init_media) || (mii->full_duplex != duplex)) {
0565 mii->full_duplex = duplex;
0566 return 1;
0567 }
0568
0569 return 0;
0570 }
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582 int generic_mii_ioctl(struct mii_if_info *mii_if,
0583 struct mii_ioctl_data *mii_data, int cmd,
0584 unsigned int *duplex_chg_out)
0585 {
0586 int rc = 0;
0587 unsigned int duplex_changed = 0;
0588
0589 if (duplex_chg_out)
0590 *duplex_chg_out = 0;
0591
0592 mii_data->phy_id &= mii_if->phy_id_mask;
0593 mii_data->reg_num &= mii_if->reg_num_mask;
0594
0595 switch(cmd) {
0596 case SIOCGMIIPHY:
0597 mii_data->phy_id = mii_if->phy_id;
0598 fallthrough;
0599
0600 case SIOCGMIIREG:
0601 mii_data->val_out =
0602 mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
0603 mii_data->reg_num);
0604 break;
0605
0606 case SIOCSMIIREG: {
0607 u16 val = mii_data->val_in;
0608
0609 if (mii_data->phy_id == mii_if->phy_id) {
0610 switch(mii_data->reg_num) {
0611 case MII_BMCR: {
0612 unsigned int new_duplex = 0;
0613 if (val & (BMCR_RESET|BMCR_ANENABLE))
0614 mii_if->force_media = 0;
0615 else
0616 mii_if->force_media = 1;
0617 if (mii_if->force_media &&
0618 (val & BMCR_FULLDPLX))
0619 new_duplex = 1;
0620 if (mii_if->full_duplex != new_duplex) {
0621 duplex_changed = 1;
0622 mii_if->full_duplex = new_duplex;
0623 }
0624 break;
0625 }
0626 case MII_ADVERTISE:
0627 mii_if->advertising = val;
0628 break;
0629 default:
0630
0631 break;
0632 }
0633 }
0634
0635 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
0636 mii_data->reg_num, val);
0637 break;
0638 }
0639
0640 default:
0641 rc = -EOPNOTSUPP;
0642 break;
0643 }
0644
0645 if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
0646 *duplex_chg_out = 1;
0647
0648 return rc;
0649 }
0650
0651 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
0652 MODULE_DESCRIPTION ("MII hardware support library");
0653 MODULE_LICENSE("GPL");
0654
0655 EXPORT_SYMBOL(mii_link_ok);
0656 EXPORT_SYMBOL(mii_nway_restart);
0657 EXPORT_SYMBOL(mii_ethtool_gset);
0658 EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
0659 EXPORT_SYMBOL(mii_ethtool_sset);
0660 EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
0661 EXPORT_SYMBOL(mii_check_link);
0662 EXPORT_SYMBOL(mii_check_media);
0663 EXPORT_SYMBOL(mii_check_gmii_support);
0664 EXPORT_SYMBOL(generic_mii_ioctl);
0665