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