1 /* 2 3 mii.c: MII interface library 4 5 Maintained by Jeff Garzik <jgarzik@pobox.com> 6 Copyright 2001,2002 Jeff Garzik 7 8 Various code came from myson803.c and other files by 9 Donald Becker. Copyright: 10 11 Written 1998-2002 by Donald Becker. 12 13 This software may be used and distributed according 14 to the terms of the GNU General Public License (GPL), 15 incorporated herein by reference. Drivers based on 16 or derived from this code fall under the GPL and must 17 retain the authorship, copyright and license notice. 18 This file is not a complete program and may only be 19 used when the entire operating system is licensed 20 under the GPL. 21 22 The author may be reached as becker@scyld.com, or C/O 23 Scyld Computing Corporation 24 410 Severn Ave., Suite 210 25 Annapolis MD 21403 26 27 28 */ 29 30 #include <linux/kernel.h> 31 #include <linux/module.h> 32 #include <linux/netdevice.h> 33 #include <linux/ethtool.h> 34 #include <linux/mdio.h> 35 36 static u32 mii_get_an(struct mii_if_info *mii, u16 addr) 37 { 38 u32 result = 0; 39 int advert; 40 41 advert = mii->mdio_read(mii->dev, mii->phy_id, addr); 42 if (advert & LPA_LPACK) 43 result |= ADVERTISED_Autoneg; 44 if (advert & ADVERTISE_10HALF) 45 result |= ADVERTISED_10baseT_Half; 46 if (advert & ADVERTISE_10FULL) 47 result |= ADVERTISED_10baseT_Full; 48 if (advert & ADVERTISE_100HALF) 49 result |= ADVERTISED_100baseT_Half; 50 if (advert & ADVERTISE_100FULL) 51 result |= ADVERTISED_100baseT_Full; 52 if (advert & ADVERTISE_PAUSE_CAP) 53 result |= ADVERTISED_Pause; 54 if (advert & ADVERTISE_PAUSE_ASYM) 55 result |= ADVERTISED_Asym_Pause; 56 57 return result; 58 } 59 60 /** 61 * mii_ethtool_gset - get settings that are specified in @ecmd 62 * @mii: MII interface 63 * @ecmd: requested ethtool_cmd 64 * 65 * Returns 0 for success, negative on error. 66 */ 67 int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) 68 { 69 struct net_device *dev = mii->dev; 70 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0; 71 u32 nego; 72 73 ecmd->supported = 74 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | 75 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | 76 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); 77 if (mii->supports_gmii) 78 ecmd->supported |= SUPPORTED_1000baseT_Half | 79 SUPPORTED_1000baseT_Full; 80 81 /* only supports twisted-pair */ 82 ecmd->port = PORT_MII; 83 84 /* only supports internal transceiver */ 85 ecmd->transceiver = XCVR_INTERNAL; 86 87 /* this isn't fully supported at higher layers */ 88 ecmd->phy_address = mii->phy_id; 89 ecmd->mdio_support = MDIO_SUPPORTS_C22; 90 91 ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII; 92 93 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); 94 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR); 95 if (mii->supports_gmii) { 96 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000); 97 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000); 98 } 99 if (bmcr & BMCR_ANENABLE) { 100 ecmd->advertising |= ADVERTISED_Autoneg; 101 ecmd->autoneg = AUTONEG_ENABLE; 102 103 ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE); 104 if (ctrl1000 & ADVERTISE_1000HALF) 105 ecmd->advertising |= ADVERTISED_1000baseT_Half; 106 if (ctrl1000 & ADVERTISE_1000FULL) 107 ecmd->advertising |= ADVERTISED_1000baseT_Full; 108 109 if (bmsr & BMSR_ANEGCOMPLETE) { 110 ecmd->lp_advertising = mii_get_an(mii, MII_LPA); 111 if (stat1000 & LPA_1000HALF) 112 ecmd->lp_advertising |= 113 ADVERTISED_1000baseT_Half; 114 if (stat1000 & LPA_1000FULL) 115 ecmd->lp_advertising |= 116 ADVERTISED_1000baseT_Full; 117 } else { 118 ecmd->lp_advertising = 0; 119 } 120 121 nego = ecmd->advertising & ecmd->lp_advertising; 122 123 if (nego & (ADVERTISED_1000baseT_Full | 124 ADVERTISED_1000baseT_Half)) { 125 ecmd->speed = SPEED_1000; 126 ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full); 127 } else if (nego & (ADVERTISED_100baseT_Full | 128 ADVERTISED_100baseT_Half)) { 129 ecmd->speed = SPEED_100; 130 ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full); 131 } else { 132 ecmd->speed = SPEED_10; 133 ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full); 134 } 135 } else { 136 ecmd->autoneg = AUTONEG_DISABLE; 137 138 ecmd->speed = ((bmcr & BMCR_SPEED1000 && 139 (bmcr & BMCR_SPEED100) == 0) ? SPEED_1000 : 140 (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10); 141 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; 142 } 143 144 mii->full_duplex = ecmd->duplex; 145 146 /* ignore maxtxpkt, maxrxpkt for now */ 147 148 return 0; 149 } 150 151 /** 152 * mii_ethtool_sset - set settings that are specified in @ecmd 153 * @mii: MII interface 154 * @ecmd: requested ethtool_cmd 155 * 156 * Returns 0 for success, negative on error. 157 */ 158 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) 159 { 160 struct net_device *dev = mii->dev; 161 162 if (ecmd->speed != SPEED_10 && 163 ecmd->speed != SPEED_100 && 164 ecmd->speed != SPEED_1000) 165 return -EINVAL; 166 if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) 167 return -EINVAL; 168 if (ecmd->port != PORT_MII) 169 return -EINVAL; 170 if (ecmd->transceiver != XCVR_INTERNAL) 171 return -EINVAL; 172 if (ecmd->phy_address != mii->phy_id) 173 return -EINVAL; 174 if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) 175 return -EINVAL; 176 if ((ecmd->speed == SPEED_1000) && (!mii->supports_gmii)) 177 return -EINVAL; 178 179 /* ignore supported, maxtxpkt, maxrxpkt */ 180 181 if (ecmd->autoneg == AUTONEG_ENABLE) { 182 u32 bmcr, advert, tmp; 183 u32 advert2 = 0, tmp2 = 0; 184 185 if ((ecmd->advertising & (ADVERTISED_10baseT_Half | 186 ADVERTISED_10baseT_Full | 187 ADVERTISED_100baseT_Half | 188 ADVERTISED_100baseT_Full | 189 ADVERTISED_1000baseT_Half | 190 ADVERTISED_1000baseT_Full)) == 0) 191 return -EINVAL; 192 193 /* advertise only what has been requested */ 194 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE); 195 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4); 196 if (mii->supports_gmii) { 197 advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000); 198 tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); 199 } 200 if (ecmd->advertising & ADVERTISED_10baseT_Half) 201 tmp |= ADVERTISE_10HALF; 202 if (ecmd->advertising & ADVERTISED_10baseT_Full) 203 tmp |= ADVERTISE_10FULL; 204 if (ecmd->advertising & ADVERTISED_100baseT_Half) 205 tmp |= ADVERTISE_100HALF; 206 if (ecmd->advertising & ADVERTISED_100baseT_Full) 207 tmp |= ADVERTISE_100FULL; 208 if (mii->supports_gmii) { 209 if (ecmd->advertising & ADVERTISED_1000baseT_Half) 210 tmp2 |= ADVERTISE_1000HALF; 211 if (ecmd->advertising & ADVERTISED_1000baseT_Full) 212 tmp2 |= ADVERTISE_1000FULL; 213 } 214 if (advert != tmp) { 215 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp); 216 mii->advertising = tmp; 217 } 218 if ((mii->supports_gmii) && (advert2 != tmp2)) 219 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2); 220 221 /* turn on autonegotiation, and force a renegotiate */ 222 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); 223 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); 224 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr); 225 226 mii->force_media = 0; 227 } else { 228 u32 bmcr, tmp; 229 230 /* turn off auto negotiation, set speed and duplexity */ 231 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); 232 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 | 233 BMCR_SPEED1000 | BMCR_FULLDPLX); 234 if (ecmd->speed == SPEED_1000) 235 tmp |= BMCR_SPEED1000; 236 else if (ecmd->speed == SPEED_100) 237 tmp |= BMCR_SPEED100; 238 if (ecmd->duplex == DUPLEX_FULL) { 239 tmp |= BMCR_FULLDPLX; 240 mii->full_duplex = 1; 241 } else 242 mii->full_duplex = 0; 243 if (bmcr != tmp) 244 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp); 245 246 mii->force_media = 1; 247 } 248 return 0; 249 } 250 251 /** 252 * mii_check_gmii_support - check if the MII supports Gb interfaces 253 * @mii: the MII interface 254 */ 255 int mii_check_gmii_support(struct mii_if_info *mii) 256 { 257 int reg; 258 259 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR); 260 if (reg & BMSR_ESTATEN) { 261 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS); 262 if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) 263 return 1; 264 } 265 266 return 0; 267 } 268 269 /** 270 * mii_link_ok - is link status up/ok 271 * @mii: the MII interface 272 * 273 * Returns 1 if the MII reports link status up/ok, 0 otherwise. 274 */ 275 int mii_link_ok (struct mii_if_info *mii) 276 { 277 /* first, a dummy read, needed to latch some MII phys */ 278 mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR); 279 if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS) 280 return 1; 281 return 0; 282 } 283 284 /** 285 * mii_nway_restart - restart NWay (autonegotiation) for this interface 286 * @mii: the MII interface 287 * 288 * Returns 0 on success, negative on error. 289 */ 290 int mii_nway_restart (struct mii_if_info *mii) 291 { 292 int bmcr; 293 int r = -EINVAL; 294 295 /* if autoneg is off, it's an error */ 296 bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR); 297 298 if (bmcr & BMCR_ANENABLE) { 299 bmcr |= BMCR_ANRESTART; 300 mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr); 301 r = 0; 302 } 303 304 return r; 305 } 306 307 /** 308 * mii_check_link - check MII link status 309 * @mii: MII interface 310 * 311 * If the link status changed (previous != current), call 312 * netif_carrier_on() if current link status is Up or call 313 * netif_carrier_off() if current link status is Down. 314 */ 315 void mii_check_link (struct mii_if_info *mii) 316 { 317 int cur_link = mii_link_ok(mii); 318 int prev_link = netif_carrier_ok(mii->dev); 319 320 if (cur_link && !prev_link) 321 netif_carrier_on(mii->dev); 322 else if (prev_link && !cur_link) 323 netif_carrier_off(mii->dev); 324 } 325 326 /** 327 * mii_check_media - check the MII interface for a duplex change 328 * @mii: the MII interface 329 * @ok_to_print: OK to print link up/down messages 330 * @init_media: OK to save duplex mode in @mii 331 * 332 * Returns 1 if the duplex mode changed, 0 if not. 333 * If the media type is forced, always returns 0. 334 */ 335 unsigned int mii_check_media (struct mii_if_info *mii, 336 unsigned int ok_to_print, 337 unsigned int init_media) 338 { 339 unsigned int old_carrier, new_carrier; 340 int advertise, lpa, media, duplex; 341 int lpa2 = 0; 342 343 /* if forced media, go no further */ 344 if (mii->force_media) 345 return 0; /* duplex did not change */ 346 347 /* check current and old link status */ 348 old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0; 349 new_carrier = (unsigned int) mii_link_ok(mii); 350 351 /* if carrier state did not change, this is a "bounce", 352 * just exit as everything is already set correctly 353 */ 354 if ((!init_media) && (old_carrier == new_carrier)) 355 return 0; /* duplex did not change */ 356 357 /* no carrier, nothing much to do */ 358 if (!new_carrier) { 359 netif_carrier_off(mii->dev); 360 if (ok_to_print) 361 netdev_info(mii->dev, "link down\n"); 362 return 0; /* duplex did not change */ 363 } 364 365 /* 366 * we have carrier, see who's on the other end 367 */ 368 netif_carrier_on(mii->dev); 369 370 /* get MII advertise and LPA values */ 371 if ((!init_media) && (mii->advertising)) 372 advertise = mii->advertising; 373 else { 374 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE); 375 mii->advertising = advertise; 376 } 377 lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA); 378 if (mii->supports_gmii) 379 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000); 380 381 /* figure out media and duplex from advertise and LPA values */ 382 media = mii_nway_result(lpa & advertise); 383 duplex = (media & ADVERTISE_FULL) ? 1 : 0; 384 if (lpa2 & LPA_1000FULL) 385 duplex = 1; 386 387 if (ok_to_print) 388 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n", 389 lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 : 390 media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 391 100 : 10, 392 duplex ? "full" : "half", 393 lpa); 394 395 if ((init_media) || (mii->full_duplex != duplex)) { 396 mii->full_duplex = duplex; 397 return 1; /* duplex changed */ 398 } 399 400 return 0; /* duplex did not change */ 401 } 402 403 /** 404 * generic_mii_ioctl - main MII ioctl interface 405 * @mii_if: the MII interface 406 * @mii_data: MII ioctl data structure 407 * @cmd: MII ioctl command 408 * @duplex_chg_out: pointer to @duplex_changed status if there was no 409 * ioctl error 410 * 411 * Returns 0 on success, negative on error. 412 */ 413 int generic_mii_ioctl(struct mii_if_info *mii_if, 414 struct mii_ioctl_data *mii_data, int cmd, 415 unsigned int *duplex_chg_out) 416 { 417 int rc = 0; 418 unsigned int duplex_changed = 0; 419 420 if (duplex_chg_out) 421 *duplex_chg_out = 0; 422 423 mii_data->phy_id &= mii_if->phy_id_mask; 424 mii_data->reg_num &= mii_if->reg_num_mask; 425 426 switch(cmd) { 427 case SIOCGMIIPHY: 428 mii_data->phy_id = mii_if->phy_id; 429 /* fall through */ 430 431 case SIOCGMIIREG: 432 mii_data->val_out = 433 mii_if->mdio_read(mii_if->dev, mii_data->phy_id, 434 mii_data->reg_num); 435 break; 436 437 case SIOCSMIIREG: { 438 u16 val = mii_data->val_in; 439 440 if (mii_data->phy_id == mii_if->phy_id) { 441 switch(mii_data->reg_num) { 442 case MII_BMCR: { 443 unsigned int new_duplex = 0; 444 if (val & (BMCR_RESET|BMCR_ANENABLE)) 445 mii_if->force_media = 0; 446 else 447 mii_if->force_media = 1; 448 if (mii_if->force_media && 449 (val & BMCR_FULLDPLX)) 450 new_duplex = 1; 451 if (mii_if->full_duplex != new_duplex) { 452 duplex_changed = 1; 453 mii_if->full_duplex = new_duplex; 454 } 455 break; 456 } 457 case MII_ADVERTISE: 458 mii_if->advertising = val; 459 break; 460 default: 461 /* do nothing */ 462 break; 463 } 464 } 465 466 mii_if->mdio_write(mii_if->dev, mii_data->phy_id, 467 mii_data->reg_num, val); 468 break; 469 } 470 471 default: 472 rc = -EOPNOTSUPP; 473 break; 474 } 475 476 if ((rc == 0) && (duplex_chg_out) && (duplex_changed)) 477 *duplex_chg_out = 1; 478 479 return rc; 480 } 481 482 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>"); 483 MODULE_DESCRIPTION ("MII hardware support library"); 484 MODULE_LICENSE("GPL"); 485 486 EXPORT_SYMBOL(mii_link_ok); 487 EXPORT_SYMBOL(mii_nway_restart); 488 EXPORT_SYMBOL(mii_ethtool_gset); 489 EXPORT_SYMBOL(mii_ethtool_sset); 490 EXPORT_SYMBOL(mii_check_link); 491 EXPORT_SYMBOL(mii_check_media); 492 EXPORT_SYMBOL(mii_check_gmii_support); 493 EXPORT_SYMBOL(generic_mii_ioctl); 494 495